package checker

import (
	"fmt"
	"math/big"
	"mi/compiler/ast"
	"mi/compiler/binder"
	"mi/compiler/diagnostic"
	"mi/compiler/performance"
	"mi/compiler/token"
	"mi/compiler/utilities"
	"mi/internal/xbase"
	"mi/internal/xmath"
	"sort"
	"strconv"
	"unicode/utf8"
)

var (
	nextSymbolId = 1
	nextNodeId   = 1
	nextFlowId   = 1
)

func GetNodeId(node ast.Node) int {
	if node.ID() == 0 {
		node.SetID(nextNodeId)
		nextNodeId++
	}
	return node.ID()
}

func GetSymbolId(symbol *binder.Symbol) int {
	if symbol.ID == 0 {
		symbol.ID = nextSymbolId
		nextSymbolId++
	}

	return symbol.ID
}

type checker struct {
	program             Program
	produceDiagnostics  bool
	noUnusedIdentifiers bool

	diagnostics         []*diagnostic.Diagnostic
	diagnosticsModified bool

	resolutionTargets       []interface{}
	resolutionResults       []bool
	resolutionGetters       []getterFunc

	symbolLinks      map[int]*symbolLinks
	nodeLinks        map[int]*nodeLinks
	numberLikeValues map[string]*big.Float
	flowLoopCaches   map[int]map[string]Type
	flowLoopNodes    map[int]binder.FlowNode
	flowLoopKeys     map[int]string
	flowLoopTypes    map[int][]Type
	visitedFlowNodes map[int]binder.FlowNode
	visitedFlowTypes map[int]Type

	viaStaticContext map[int64]bool

	deferredNodes    []ast.Node
	flowLoopStart    int
	flowLoopCount    int
	visitedFlowCount int

	emptySymbols         *binder.SymbolTable
	emptySymbolArray     []*binder.Symbol
	emptyNodeArray       []ast.Node
	emptyExpressionArray []ast.Expression
	emptySignatureArray  []*Signature
	emptyBaseTypeArray  []BaseType

	inBoundsTypes      	map[int]*inBoundsType  // ? super E
	outBoundsTypes     	map[int]*outBoundsType // ? extends E
	notNullTypes       	map[int]*notNullType
	stringLiteralTypes 	map[string]*stringLiteralType
	charLiteralTypes 	map[string]*numberLiteralType
	digitalLiteralTypes map[string]*numberLiteralType
	decimalLiteralTypes map[string]*numberLiteralType

	unknownSymbol *binder.Symbol

	autoType      ObjectType
	shortType     *intrinsicType
	intType       *intrinsicType
	longType      *intrinsicType
	charType      *intrinsicType
	byteType      *intrinsicType
	floatType     *intrinsicType
	doubleType    *intrinsicType
	trueType      *intrinsicType
	falseType     *intrinsicType
	booleanType   *intrinsicType
	voidType      *intrinsicType
	neverType     *intrinsicType
	nullType      *intrinsicType
	unknownType   *intrinsicType
	undefinedType *intrinsicType
	noBoundsType  *noBoundsType

	unknownObjectType ObjectType
	anyFunctionType   ObjectType
	noConstraintType  ObjectType

	anySignature       *Signature
	resolvingSignature *Signature
	unknownSignature   *Signature


	globalObjectType            ObjectType
	globalClassType             ObjectType
	globalArrayType             ObjectType
	globalStringType            ObjectType
	globalIterableType          ObjectType
	globalThrowableType         ObjectType
	globalErrorType             ObjectType
	globalExceptionType         ObjectType
	globalAnnotationType        ObjectType
	globalRuntimeExceptionType  ObjectType
	globalTargetAnnotationType  ObjectType
	globalElementType           ObjectType
	globalFunctionInterfaceType ObjectType
	globalByteType				ObjectType
	globalBooleanType			ObjectType
	globalCharacterType			ObjectType
	globalShortType				ObjectType
	globalIntegerType			ObjectType
	globalLongType				ObjectType
	globalFloatType				ObjectType
	globalDoubleType			ObjectType

	defaultImportPackageSymbols []*binder.Symbol
	identityMapper              *TypeMapper
	typeCount                   int

	subtypeRelation    *relatedCache
	assignableRelation *relatedCache
	comparableRelation *relatedCache
	identityRelation   *relatedCache
}

func CreateTypeChecker(program Program, produceDiagnostics bool) TypeChecker {
	var checker = new(checker)
	checker.program = program
	checker.produceDiagnostics = produceDiagnostics
	checker.initializeTypeChecker()
	return checker
}

func (c *checker) initializeTypeChecker() {
	// Initialize members
	c.symbolLinks 	    = make(map[int]*symbolLinks)
	c.nodeLinks 		= make(map[int]*nodeLinks)
	c.numberLikeValues  = make(map[string]*big.Float)

	c.flowLoopCaches 	= make(map[int]map[string]Type)
	c.flowLoopNodes		= make(map[int]binder.FlowNode)
	c.flowLoopKeys 		= make(map[int]string)
	c.flowLoopTypes		= make(map[int][]Type)
	c.visitedFlowNodes 	= make(map[int]binder.FlowNode)
	c.visitedFlowTypes 	= make(map[int]Type)

	c.viaStaticContext = make(map[int64]bool)

	c.emptySymbols 		= binder.NewSymbolTable()
	c.emptySymbolArray 	= make([]*binder.Symbol, 0)
	c.emptyNodeArray 	= make([]ast.Node, 0)
	c.emptyExpressionArray  = make([]ast.Expression, 0)
	c.emptySignatureArray 	= make([]*Signature, 0)
	c.emptyBaseTypeArray    = make([]BaseType, 0)


	c.inBoundsTypes 		= make(map[int]*inBoundsType)
	c.outBoundsTypes 		= make(map[int]*outBoundsType)
	c.notNullTypes 		    = make(map[int]*notNullType)
	c.stringLiteralTypes 	= make(map[string]*stringLiteralType)
	c.charLiteralTypes 		= make(map[string]*numberLiteralType)
	c.digitalLiteralTypes 	= make(map[string]*numberLiteralType)
	c.decimalLiteralTypes 	= make(map[string]*numberLiteralType)

	c.unknownSymbol = newSymbol(binder.Field, "unknown")

	c.charType 	  	= c.newIntrinsicTypeWithToken(TFChar, token.CharKeyword)
	c.byteType 	  	= c.newIntrinsicTypeWithToken(TFByte, token.ByteKeyword)
	c.shortType   	= c.newIntrinsicTypeWithToken(TFShort, token.ShortKeyword)
	c.intType     	= c.newIntrinsicTypeWithToken(TFInt, token.IntKeyword)
	c.longType    	= c.newIntrinsicTypeWithToken(TFLong, token.LongKeyword)
	c.floatType   	= c.newIntrinsicTypeWithToken(TFFloat, token.FloatKeyword)
	c.doubleType  	= c.newIntrinsicTypeWithToken(TFDouble, token.DoubleKeyword)
	c.trueType    	= c.newIntrinsicTypeWithToken(TFBoolean, token.TrueKeyword)
	c.falseType   	= c.newIntrinsicTypeWithToken(TFBoolean, token.FalseKeyword)
	c.booleanType 	= c.newIntrinsicTypeWithToken(TFBoolean, token.BooleanKeyword)
	c.voidType    	= c.newIntrinsicTypeWithToken(TFVoid, token.VoidKeyword)
	c.nullType    	= c.newIntrinsicTypeWithToken(TFNull, token.NullKeyword)
	c.unknownType   = c.newIntrinsicType(TFAny, "未知", "unknown")
	c.undefinedType = c.newIntrinsicType(TFAny, "未定义", "undefined")
	c.noBoundsType  = c.newNoBoundsType(TFNoBounds)

	c.unknownObjectType = c.createAnonymousType(nil, c.emptySymbols, c.emptySignatureArray, c.emptySignatureArray)
	c.anyFunctionType   = c.createAnonymousType(nil, c.emptySymbols, c.emptySignatureArray, c.emptySignatureArray)
	c.noConstraintType  = c.createAnonymousType(nil, c.emptySymbols, c.emptySignatureArray, c.emptySignatureArray)

	c.anySignature       = c.createSignature(nil, nil, nil, nil, c.unknownType, 0, false)
	c.resolvingSignature = c.createSignature(nil, nil, nil, nil, c.unknownType, 0, false)
	c.unknownSignature   = c.createSignature(nil, nil, nil, nil, c.unknownType, 0, false)

	c.identityMapper = &TypeMapper{Invoke: func(t Type) Type { return t }}

	c.subtypeRelation    = newRelatedCache(subtypeRelation)
	c.assignableRelation = newRelatedCache(assignableRelation)
	c.comparableRelation = newRelatedCache(comparableRelation)
	c.identityRelation   = newRelatedCache(identityRelation)
	c.initializePrimitiveRelation()

	// Initialize special symbols
	c.getSymbolLinks(c.unknownSymbol).tpe = c.unknownType

	// Default import packages
	c.defaultImportPackageSymbols = append(c.defaultImportPackageSymbols, c.getPackageSymbolInDefaultPackage("java.lang"))
	c.defaultImportPackageSymbols = append(c.defaultImportPackageSymbols, c.getPackageSymbolInDefaultPackage("java.io"))

	// Initialize special types
	c.globalObjectType = c.getTypeInDefaultPackage("java.lang", "Object",0, binder.Class)
	c.globalArrayType  = c.getTypeInDefaultPackage("java.lang", "Array",1, binder.Class)
	c.globalClassType = c.getTypeInDefaultPackage("java.lang", "Class",1, binder.Class)
	c.globalStringType = c.getTypeInDefaultPackage("java.lang", "String",0, binder.Class)
	c.globalStringType.orFlags(TFString)

	c.globalThrowableType 		  = c.getTypeInDefaultPackage("java.lang", "Throwable", 0, binder.Class)
	c.globalErrorType 			  = c.getTypeInDefaultPackage("java.lang", "Error", 0, binder.Class)
	c.globalExceptionType 		  = c.getTypeInDefaultPackage("java.lang", "Exception", 0, binder.Class)
	c.globalRuntimeExceptionType  = c.getTypeInDefaultPackage("java.lang", "RuntimeException", 0, binder.Class)
	c.globalIterableType 		  = c.getTypeInDefaultPackage("java.lang", "Iterable", 1, binder.Interface)
	c.globalAnnotationType 		  = c.getTypeInDefaultPackage("java.lang.annotation", "Annotation", 0, binder.Interface)
	c.globalTargetAnnotationType  = c.getTypeInDefaultPackage("java.lang.annotation", "Target", 0, binder.Annotation)
	c.globalElementType           = c.getTypeInDefaultPackage("java.lang.annotation", "ElementType", 0, binder.Enum)
	c.globalFunctionInterfaceType = c.getTypeInDefaultPackage("java.lang", "FunctionalInterface", 0, binder.Annotation)
	c.globalByteType			  = c.getTypeInDefaultPackage("java.lang", "Byte", 0, binder.Class)
	c.globalBooleanType			  = c.getTypeInDefaultPackage("java.lang", "Boolean", 0, binder.Class)
	c.globalCharacterType		  = c.getTypeInDefaultPackage("java.lang", "Character", 0, binder.Class)
	c.globalShortType			  = c.getTypeInDefaultPackage("java.lang", "Short", 0, binder.Class)
	c.globalIntegerType			  = c.getTypeInDefaultPackage("java.lang", "Integer", 0, binder.Class)
	c.globalLongType			  = c.getTypeInDefaultPackage("java.lang", "Long", 0, binder.Class)
	c.globalFloatType			  = c.getTypeInDefaultPackage("java.lang", "Float", 0, binder.Class)
	c.globalDoubleType			  = c.getTypeInDefaultPackage("java.lang", "Double", 0, binder.Class)
	c.initializePrimitiveClassRelation()
}

func (c *checker) initializePrimitiveRelation() {
	c.assignableRelation.set(c.byteType.id, c.shortType.id, 1)
	c.assignableRelation.set(c.byteType.id, c.intType.id, 2)
	c.assignableRelation.set(c.shortType.id, c.intType.id, 2)
	c.assignableRelation.set(c.charType.id, c.intType.id, 2)

	c.assignableRelation.set(c.byteType.id, c.longType.id, 3)
	c.assignableRelation.set(c.shortType.id, c.longType.id, 2)
	c.assignableRelation.set(c.charType.id, c.longType.id, 2)
	c.assignableRelation.set(c.intType.id, c.longType.id, 1)

	c.assignableRelation.set(c.byteType.id, c.floatType.id, 4)
	c.assignableRelation.set(c.shortType.id, c.floatType.id, 3)
	c.assignableRelation.set(c.charType.id, c.floatType.id, 3)
	c.assignableRelation.set(c.intType.id, c.floatType.id, 2)
	c.assignableRelation.set(c.longType.id, c.floatType.id, 1)

	c.assignableRelation.set(c.byteType.id, c.doubleType.id, 5)
	c.assignableRelation.set(c.shortType.id, c.doubleType.id, 4)
	c.assignableRelation.set(c.charType.id, c.doubleType.id, 4)
	c.assignableRelation.set(c.intType.id, c.doubleType.id, 3)
	c.assignableRelation.set(c.longType.id, c.doubleType.id, 2)
	c.assignableRelation.set(c.floatType.id, c.doubleType.id, 1)
}

func (c *checker) boxingType(tpe Type) Type {
	if tpe == c.booleanType {
		return c.globalBooleanType
	} else if tpe == c.byteType {
		return c.globalByteType
	} else if tpe == c.charType {
		return c.globalCharacterType
	} else if tpe == c.shortType {
		return c.globalShortType
	} else if tpe == c.intType {
		return c.globalIntegerType
	} else if tpe == c.longType {
		return c.globalLongType
	} else if tpe == c.floatType {
		return c.globalFloatType
	} else if tpe == c.doubleType {
		return c.globalDoubleType
	}

	return tpe
}

func (c *checker) unBoxingType(tpe Type) Type {
	if tpe == c.globalBooleanType {
		return c.booleanType
	} else if tpe == c.globalByteType {
		return c.byteType
	} else if tpe == c.globalCharacterType {
		return c.charType
	} else if tpe == c.globalShortType {
		return c.shortType
	} else if tpe == c.globalIntegerType {
		return c.intType
	} else if tpe == c.globalLongType {
		return c.longType
	} else if tpe == c.globalFloatType {
		return c.floatType
	} else if tpe == c.globalDoubleType {
		return c.doubleType
	}

	return tpe
}

func (c *checker) getBoxingTypes(types []Type) []Type {
	var result []Type
	for _, tpe := range types {
		result = append(result, c.boxingType(tpe))
	}
	return result
}

const boxingDifference = 10
func (c *checker) initializePrimitiveClassRelation() {
	// Primitive type assigned to boxing type
	c.assignableRelation.set(c.booleanType.id, c.globalBooleanType.ID(), boxingDifference)
	c.assignableRelation.set(c.byteType.id, c.globalByteType.ID(), boxingDifference)
	c.assignableRelation.set(c.charType.id, c.globalCharacterType.ID(), boxingDifference)
	c.assignableRelation.set(c.shortType.id, c.globalShortType.ID(), boxingDifference)
	c.assignableRelation.set(c.intType.id, c.globalIntegerType.ID(), boxingDifference)
	c.assignableRelation.set(c.longType.id, c.globalLongType.ID(), boxingDifference)
	c.assignableRelation.set(c.floatType.id, c.globalFloatType.ID(), boxingDifference)
	c.assignableRelation.set(c.doubleType.id, c.globalDoubleType.ID(), boxingDifference)

	// Boxing type assigned to Primitive type
	c.assignableRelation.set(c.globalBooleanType.ID(), c.booleanType.id, boxingDifference)
	c.assignableRelation.set(c.globalByteType.ID(), c.byteType.id, boxingDifference)
	c.assignableRelation.set(c.globalCharacterType.ID(), c.charType.id, boxingDifference)
	c.assignableRelation.set(c.globalShortType.ID(), c.shortType.id, boxingDifference)
	c.assignableRelation.set(c.globalIntegerType.ID(), c.intType.id, boxingDifference)
	c.assignableRelation.set(c.globalLongType.ID(), c.longType.id, boxingDifference)
	c.assignableRelation.set(c.globalFloatType.ID(), c.floatType.id, boxingDifference)
	c.assignableRelation.set(c.globalDoubleType.ID(), c.doubleType.id, boxingDifference)
}

func (c *checker) getIndexTypeOfType(t Type, kind interface{}) Type {
	panic("implement me")
}

func (c *checker) getProgram() Program {
	return c.program
}

func (c *checker) addDiagnostic(diagnostic *diagnostic.Diagnostic) {
	c.diagnostics = append(c.diagnostics, diagnostic)
	c.diagnosticsModified = true
}

func (c *checker) error(location ast.Node, message *diagnostic.Message, args ...interface{}) {
	var diag *diagnostic.Diagnostic
	if location != nil {
		diag = utilities.CreateDiagnosticForNode(location, message, args...)
	} else {
		diag = diagnostic.CreateCompilerDiagnostic(message, args...)
	}
	c.addDiagnostic(diag)
}

func (c *checker) getExcludedSymbolFlags(flags binder.SymbolFlags) binder.SymbolFlags {
	var result = binder.None
	if flags&binder.BlockScopedVariable != 0 {
		result |= binder.BlockScopedVariableExcludes
	}
	if flags&binder.Field != 0 {
		result |= binder.FieldExcludes
	}
	if flags&binder.EnumMember != 0 {
		result |= binder.EnumMemberExcludes
	}
	if flags&binder.Class != 0 {
		result |= binder.InterfaceExcludes
	}
	if flags&binder.EnumMember != 0 {
		result |= binder.EnumMemberExcludes
	}
	if flags&binder.Method != 0 {
		result |= binder.MethodExcludes
	}
	if flags&binder.TypeParameter != 0 {
		result |= binder.TypeParameterExcludes
	}
	return result
}

func (c *checker) getSymbolLinks(symbol *binder.Symbol) *symbolLinks {
	GetSymbolId(symbol)
	if c.symbolLinks[symbol.ID] == nil {
		c.symbolLinks[symbol.ID] = new(symbolLinks)
	}
	return c.symbolLinks[symbol.ID]
}

func (c *checker) getNodeLinks(node ast.Node) *nodeLinks {
	GetNodeId(node)
	if c.nodeLinks[node.ID()] == nil {
		c.nodeLinks[node.ID()] = new(nodeLinks)
	}
	return c.nodeLinks[node.ID()]
}

func (c *checker) getInBoundsType(tpe Type) *inBoundsType {
	switch t := tpe.(type) {
	case *inBoundsType:
		return t
	default:
		if c.inBoundsTypes[tpe.ID()] == nil {
			c.inBoundsTypes[tpe.ID()] = c.newInBoundsType(TFInBounds, tpe)
		}
		return c.inBoundsTypes[tpe.ID()]
	}
}

func (c *checker) getOutBoundsType(tpe Type) *outBoundsType {
	switch t := tpe.(type) {
	case *outBoundsType:
		return t
	default:
		if c.outBoundsTypes[tpe.ID()] == nil {
			c.outBoundsTypes[tpe.ID()] = c.newOutBoundsType(TFOutBounds, tpe)
		}
		return c.outBoundsTypes[tpe.ID()]
	}
}

func (c *checker) getNullableType(tpe Type) Type {
	switch t := tpe.(type) {
	case *notNullType:
		return t.target
	default:
		return t
	}
}

func (c *checker) getNotNullType(tpe Type) *notNullType {
	switch t := tpe.(type) {
	case *notNullType:
		return t
	default:
		if c.notNullTypes[tpe.ID()] == nil {
			c.notNullTypes[tpe.ID()] = c.newNotNullType(TFNullable, tpe)
		}
		return c.notNullTypes[tpe.ID()]
	}
}

func (c *checker) isLiteralType(tpe Type) bool {
	if isNull(tpe) {
		return false
	}

	if tpe == c.trueType || tpe == c.falseType {
		return true
	}

	switch tpe.(type) {
	case *stringLiteralType,
		*numberLiteralType:
		return true
	}

	return false
}

func (c *checker) isPrimitiveNumeric(tpe Type) bool {
	return tpe == c.booleanType ||
		tpe == c.byteType ||
		tpe == c.charType ||
		tpe == c.shortType ||
		tpe == c.intType ||
		tpe == c.longType ||
		tpe == c.floatType ||
		tpe == c.doubleType
}

func (c *checker) getCheckFlags(symbol *binder.Symbol) checkFlags {
	if symbol.Flags&binder.Transient != 0 {
		return c.getSymbolLinks(symbol).checkFlags
	}
	return 0
}

func (c *checker) getAliasSymbolMappedDeclaration(symbol *binder.Symbol) ast.Declaration {
	var location ast.Node = symbol.Declarations[0]
	for location != nil {
		switch location.(type) {
		case *ast.ClassDeclaration,
			*ast.EnumDeclaration,
			*ast.InterfaceDeclaration,
			*ast.AnnotationDeclaration,
			*ast.FieldDeclaration,
			*ast.MethodDeclaration:
			return location.(ast.Declaration)
		}
		location = location.Parent()
	}

	panic("Not run to here in 'getAliasSymbolMappedDeclaration'")
}

func (c *checker) getAliasSymbolMappedName(symbol *binder.Symbol) string {
	var mappedDeclaration = c.getAliasSymbolMappedDeclaration(symbol)
	var declarationName = ast.GetDeclarationName(mappedDeclaration)
	assert(declarationName != nil)
	return declarationName.Value
}

func (c *checker) getSymbol(symbols *binder.SymbolTable, name string, meaning binder.SymbolFlags) *binder.Symbol {
	for _, symbol := range symbols.GetSymbolsOfName(name, meaning) {
		if (c.getCheckFlags(symbol)& instantiated) != 0 {
			panic("Should never get an instantiated symbol here.")
		}
		if symbol.Flags&meaning != 0 && symbol.Flags&binder.Duplicate == 0{
			return symbol
		}
	}

	// return nil if we can't find a symbol.
	return nil
}

func (c *checker) getResolvedSymbolPath(symbol *binder.Symbol) string {
	var links = c.getSymbolLinks(symbol)
	if links.path == nil {
		if symbol.Flags&binder.Type == 0 {
			links.path = xbase.NewString("")
		} else {
			var pkg = ast.GetPackageOfNode(symbol.Declarations[0])
			links.path = xbase.NewString(c.program.GetPackagePath(pkg) + "." + symbol.Name)
		}
	}
	return *links.path
}

func (c *checker) getResolvedSourceFileImportSymbols(node ast.Node) []*binder.Symbol{
	var sourceFile = ast.GetSourceFileOfNode(node)
	var links = c.getNodeLinks(sourceFile)
	if links.resolvedImportSymbols == nil {
		for i := 0; i < sourceFile.Imports.Len(); i ++ {
			var importDecl = sourceFile.Imports.At(i).(*ast.ImportDeclaration)
			var symbol = c.getResolvedImportSymbol(importDecl)
			if symbol != nil && symbol != c.unknownSymbol {
				var file = ast.GetSourceFileOfNode(node)
				var links = c.getNodeLinks(file)
				links.resolvedImportSymbols = append(links.resolvedImportSymbols, symbol)
			}
		}

		if links.resolvedImportSymbols == nil {
			links.resolvedImportSymbols = c.emptySymbolArray
		}
	}
	return links.resolvedImportSymbols
}

func (c *checker) getPackageSymbol(location ast.Node, path string) *binder.Symbol {
	var project = ast.GetProjectFileOfNode(location)
	var tree = c.program.GetPackageTree(path)
	var pkg = c.program.GetPackage(project, tree)
	if pkg != nil {
		return binder.GetSymbolOfNode(pkg)
	}
	return nil
}

// !!!
func (c *checker) getPackageTreeOfSymbol(symbol *binder.Symbol) *ast.PackageTree {
	if symbol.Flags&binder.Package != 0 {
		var pkgDecl = symbol.Declarations[0].(*ast.Package)
		return pkgDecl.Tree
	}
	return nil
}

func (c *checker) isNodeToSymbolViaStaticContext(node ast.Node, symbol *binder.Symbol) bool {
	var uuid = int64(GetNodeId(node))<<32 | int64(GetSymbolId(symbol))
	return c.viaStaticContext[uuid]
}

func (c *checker) setNodeToSymbolViaStaticContext(node ast.Node, symbol *binder.Symbol, value bool) {
	var uuid = int64(GetNodeId(node))<<32 | int64(GetSymbolId(symbol))
	c.viaStaticContext[uuid] = value
}

func (c *checker) findInImportSymbols(location ast.Node, name string, meaning binder.SymbolFlags) *binder.Symbol {
	var result *binder.Symbol
	for _, symbol := range c.getResolvedSourceFileImportSymbols(location) {
		if symbol.Flags&binder.Package != 0 {
			result = c.getSymbol(symbol.Exports, name, meaning)
		} else {
			var resolved = c.resolveSymbol(symbol)
			if resolved.Name == name && resolved.Flags&meaning != 0 {
				result = resolved
			}
		}

		if result != nil {
			return result
		}
	}
	return nil
}

// TODO: implement it
func (c *checker) findInDefaultImport(name string, meaning binder.SymbolFlags) *binder.Symbol {
	var result *binder.Symbol
	for _, symbol := range c.defaultImportPackageSymbols {
		result = c.getSymbol(symbol.Exports, name, meaning)
		if result != nil {
			return result
		}
	}
	return nil
}

func (c *checker) inObjectHeritageClause(object ast.ObjectLike, node ast.Node) bool {
	var current = node

	var heritageClauses = object.GetHeritageClauses()
	if heritageClauses == nil {
		 return false
	}

	for !isNull(current) && current != object {
		if heritageClauses.NodeIndex(current) != -1 {
			return true
		}

		current = current.Parent()
	}

	return false
}

// Find in the class declaration and return directly, Need to traverse all eligible symbols in the interface declaration
func (c *checker) resolveNameInHeritage(symbol *binder.Symbol, name string, meaning binder.SymbolFlags, setViaStaticContext func(symbol *binder.Symbol)) []*binder.Symbol {
	if symbol == nil {
		return nil
	}

	var result []*binder.Symbol
	switch n := symbol.ValueDeclaration.(type) {
	case *ast.ClassDeclaration,
		*ast.InterfaceDeclaration,
		*ast.BodyDeclaration:

		if member := c.getSymbol(symbol.Members, name, meaning); member != nil {
			// Private members will not be inherited, return nil and stop searching
			if c.getDeclarationModifierFlagsFromSymbol(member)&ast.MFPrivate != 0 {
				return nil
			}

			setViaStaticContext(member)
			result = append(result, member)

			// Find in the class declaration and return directly
			if ast.IsClassLike(n) {
				break
			}
		}

		for _, baseType := range c.getBaseTypes(c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)) {
			if baseType.Kind == BTKExtends {
				var baseSymbol = baseType.Type.Symbol()
				result = append(result, c.resolveNameInHeritage(baseSymbol, name, meaning, setViaStaticContext)...)
			}
		}
	}

	return result
}

// Warning: when resolveMember is false, it can only be used to find the type
func (c *checker) resolveName(location ast.Node, name string, meaning binder.SymbolFlags, errIdentifier *ast.Identifier,
	nameNotFoundMessage *diagnostic.Message, nameArg string) *binder.Symbol {

	var result *binder.Symbol
	var inStaticContext = false

	var checkResultSymbol = func () bool {
		if result == nil {
			return false
		}

		if result.Flags&binder.BlockScopedVariable != 0 {
			// checkBlockScopeVariablePosition
			if  location.Pos() <= result.ValueDeclaration.Pos() {
				result = nil
				return false
			}
		}
		return result != nil
	}

	var error = func(message *diagnostic.Message, args ...interface{}) {
		if !isNull(errIdentifier) {
			c.error(errIdentifier, message, args...)
		}
	}

	var setViaStaticContext = func(symbol *binder.Symbol) {
		if !isNull(errIdentifier) {
			c.setNodeToSymbolViaStaticContext(errIdentifier, symbol, inStaticContext)
		}
	}

	var current = location
	loop: for !isNull(current) {
		// Locals of a session file are not in scope (because they get merged into the global symbol table)
		var locals = binder.GetLocalsOfNode(current)
		if locals != nil {
			if result = c.getSymbol(locals, name, meaning); checkResultSymbol() {
				break loop
			}
		}

		switch current.(type) {
		case *ast.MethodDeclaration:
			if !inStaticContext && ast.GetModifierFlags(current)&ast.MFStatic != 0 {
				inStaticContext = true
			}

		case *ast.EnumDeclaration,
			*ast.ClassDeclaration,
			*ast.InterfaceDeclaration,
			*ast.AnnotationDeclaration,
			*ast.BodyDeclaration:

			var symbol = c.getSymbolOfNode(current)

			if meaning&^(binder.Type|binder.Namespace) != 0 {
				var declarationType = c.getDeclaredTypeOfSymbol(symbol)
				if result = c.getMemberOfType(declarationType, name, meaning); result != nil {
					setViaStaticContext(result)
					break loop
				}

			} else {
				if c.inObjectHeritageClause(current.(ast.ObjectLike), location) {
					// If only type parameters can be found in the heritage clause
					if result = c.getSymbol(symbol.Members, name, meaning&binder.TypeParameter); result != nil {
						break loop
					}
					// Jump out of the current switch clause
					break
				}

				// When looking up the type, you should not resolve the members of the object, which will cause a recursive loop
				if result = c.getSymbol(symbol.Members, name, meaning); result != nil {
					setViaStaticContext(result)
					break loop
				}

				if symbols := c.resolveNameInHeritage(symbol, name, meaning, setViaStaticContext); len(symbols) > 0 {
					result = symbols[0]

					// Ambiguous results, report errors
					if len(symbols) > 1 {
						// In ‘symbolToString’, ‘resolveName’ may be referenced again, so set ‘enclosingDeclaration’ to nil
						error(diagnostic.M_Reference_to_0_is_ambiguous_both_1_and_2_match, name,
							c.symbolToString(symbols[0].Parent, nil, 0, true),
							c.symbolToString(symbols[1].Parent, nil, 0, true))
					}

					break loop
				}

			}

			if !(ast.IsBodyDeclaration(current) || ast.IsClassLike(current) && ast.GetModifierFlags(current)&ast.MFStatic == 0){
				inStaticContext = true
			}
		}

		current = current.Parent()
	}

	if !isNull(errIdentifier) && result != nil && c.isNodeToSymbolViaStaticContext(errIdentifier, result) {
		if result.Flags&(binder.Class|binder.Field) != 0 &&
			c.getDeclarationModifierFlagsFromSymbol(result)&ast.MFStatic == 0 {
			// In 'symbolToString', 'resolveName' may be referenced again, so set ‘enclosingDeclaration’ to nil
			error(diagnostic.M_Non_static_0_cannot_be_referenced_from_a_static_context,
				c.symbolToString(result, nil, 0, true))
		}
	}

	// Find in import declaration
	if isNull(result) {
		result = c.findInImportSymbols(location, name, meaning)
	}

	// Find in default import
	if isNull(result) {
		result = c.findInDefaultImport(name, meaning)
	}

	// Find package in package tree
	if isNull(result) && meaning&binder.Package != 0 {
		result = c.getPackageSymbol(location, name)
	}

	// Report error
	if isNull(result) {
		error(nameNotFoundMessage, nameArg)
	}

	return result
}

func (c *checker) resolveNameNoReportError(location ast.Node, name string, meaning binder.SymbolFlags) *binder.Symbol {
	return c.resolveName(location, name, meaning, nil, nil, "")
}

func (c *checker) resolveSymbol(symbol *binder.Symbol) *binder.Symbol {
	if symbol.Flags&binder.AliasLike != 0 {
		var link = c.getSymbolLinks(symbol)
		if link.mappedSymbol != nil {
			return link.mappedSymbol
		}

		// Original symbol
		var declaration = c.getAliasSymbolMappedDeclaration(symbol)
		return c.getSymbolOfNode(declaration)
	}

	return symbol
}

func (c *checker) resolveSymbolFromParameterTagIdentifier(identifier *ast.Identifier) *binder.Symbol {
	var parameterTag = identifier.Parent().(*ast.ParameterTag)
	var active = ast.GetDocActiveNode(parameterTag)
	if ast.IsFunctionLike(active) {
		var signature = c.getSignatureFromDeclaration(active.(ast.FunctionLike))
		if parameterTag.IsTypeParameter {
			for _, parameterType := range signature.TypeParameters {
				if !isNull(parameterType.symbol) && parameterType.symbol.Name == identifier.Value {
					return parameterType.symbol
				}
			}
		} else {
			for _, parameter := range signature.Parameters {
				if parameter.Name == identifier.Value {
					return parameter
				}
			}
		}
	}

	if ast.IsClassDeclaration(active) || ast.IsInterfaceDeclaration(active) {
		var tpe = c.getDeclaredTypeOfSymbol(binder.GetSymbolOfNode(active))
		if genericType, ok := tpe.(*genericType); ok {
			for _, param := range genericType.typeParams {
				if param.symbol.Name == identifier.Value {
					return param.symbol
				}
			}
		}
	}

	return nil
}

/* Starting from 'initial' ast walk up the parent chain until 'stopAt' ast is reached.
 * If at any point current ast is equal to 'parent' ast - return true.
 * Return false if 'stopAt' ast is reached or isFunctionLike(current) === true.
 */
func (c *checker) isSameScopeDescendentOf(initial ast.Node, parent ast.Node, stopAt ast.Node) bool {
	if isNull(parent) {
		return false
	}
	for current := initial; current != nil && current != stopAt && !ast.IsFunctionLike(current); current = current.Parent() {
		if current == parent {
			return true
		}
	}
	return false
}

func (c *checker) getFullyQualifiedName(symbol *binder.Symbol) string {
	if !isNull(symbol.Parent) {
		return c.getFullyQualifiedName(symbol.Parent) + "." + c.symbolToString(symbol, nil, 0, false)
	} else {
		return c.symbolToString(symbol, nil, 0, false)
	}
}

func (c *checker) getFullEntityName(name ast.Node) string {
	switch n := name.(type) {
	case *ast.Identifier:
		return n.Value
	case *ast.QualifiedName:
		return c.getFullEntityName(n.Left) + "." + n.Right.Value
	case *ast.SelectorExpression:
		return c.getFullEntityName(n.Expression) + "." + n.Name.Value
	}
	return ""
}

func (c *checker) checkTopLevelSymbolAccessibility(location ast.Node, symbol *binder.Symbol, reportErrors bool) bool {
	if binder.IsTopLevelSymbol(symbol) {
		var flags = c.getDeclarationModifierFlagsFromSymbol(symbol)
		// Public properties are otherwise accessible.
		if flags&ast.MFNonPublicAccessibilityModifier != 0 {
			// Must on same package
			var decl = symbol.ValueDeclaration
			if ast.GetPackageOfNode(decl) != ast.GetPackageOfNode(location) {
				if reportErrors {
					c.error(location, diagnostic.M_0_not_public_and_only_accessible_on_same_package,
						c.symbolToString(symbol, nil, 0, true))
				}
				return false
			}
		}
	}

	return true
}

// Resolves a qualified name and any involved import aliases
// Mark parameter mark the first time
const notFirstEntry = 0
func (c *checker) resolveEntityName(location ast.Node, name ast.Node,
	meaning binder.SymbolFlags, errorNode ast.Node, mark ... int) *binder.Symbol {
	if ast.NodeIsMissing(name) {
		return nil
	}

	var symbol *binder.Symbol
	switch n := name.(type) {
	case *ast.Identifier:
		// TODO: Investigate error recovery for symbols not found
		symbol = c.resolveName(location, n.Value, meaning, n,
			diagnostic.M_Cannot_find_name_0, ast.DeclarationNameToString(name))
		if isNull(symbol) {
			return nil
		}

	case *ast.QualifiedName,
		*ast.SelectorExpression:
		var left ast.Node
		var right *ast.Identifier

		if ast.IsQualifiedName(n) {
			var cur = n.(*ast.QualifiedName)
			left = cur.Left
			right = cur.Right
		} else {
			var cur = n.(*ast.SelectorExpression)
			left = cur.Expression
			right = cur.Name
		}

		var namespace = c.resolveEntityName(location, left, binder.Namespace, errorNode, notFirstEntry)
		if namespace == nil || namespace == c.unknownSymbol || ast.NodeIsMissing(right) {
			return nil
		}

		if namespace.Flags&binder.Package != 0 {
			symbol = c.getSymbol(namespace.Exports, right.Value, meaning)
		} else {
			var tpe = c.getTypeOfSymbol(namespace)
			symbol = c.getMemberOfType(tpe, right.Value, meaning)
		}

		if symbol == nil && namespace.Flags&binder.Package != 0 {
			symbol = c.getPackageSymbol(location, c.getFullEntityName(name))
		}

		if symbol != nil {
			// Check symbol accessibility
			// Ignore package symbol
			if symbol.Flags&binder.Package == 0 {
				if binder.IsTopLevelSymbol(symbol) {
					c.checkTopLevelSymbolAccessibility(right, symbol, !isNull(errorNode))
				} else {
					c.checkMemberAccessibility(name, left, symbol, !isNull(errorNode))
				}
			}
		}

		if symbol == nil {
			c.error(errorNode, diagnostic.M_Namespace_0_has_no_find_1, c.getFullyQualifiedName(namespace),
				ast.DeclarationNameToString(right))
			return nil
		}

	default:
		panic("Unknown entity name kind.")
	}

	if c.getCheckFlags(symbol) & instantiated != 0 {
		panic("Should never get an instantiated symbol here.")
	}

	if symbol.Flags&meaning != 0 ||
		// If not first entry, allow package symbol result
		isNull(mark) && symbol.Flags&binder.Package != 0 {
		// Record resolved symbol, Will be used in emit
		c.getNodeLinks(name).resolvedSymbol = symbol
		return symbol
	}
	return nil
}

func (c *checker) getSymbolOfNode(node ast.Node) *binder.Symbol {
	return binder.GetSymbolOfNode(node)
}

func (c *checker) getParentOfSymbol(symbol *binder.Symbol) *binder.Symbol {
	return symbol.Parent
}

func (c *checker) getFirstDeclarationOfSymbol(symbol *binder.Symbol) ast.Declaration {
	if len(symbol.Declarations) > 0 {
		return symbol.Declarations[0]
	}
	return nil
}

func (c *checker) findConstructorDeclaration(node ast.Declaration) *ast.ConstructorDeclaration {
	var members *ast.DeclarationList
	switch n := node.(type) {
	case *ast.ClassDeclaration:
		members = n.Members
	case *ast.EnumDeclaration:
		members = n.Members
	default:
		return nil
	}

	for _, member := range members.Array() {
		if ast.IsConstructorDeclaration(member) {
			return member.(*ast.ConstructorDeclaration)
		}
	}
	return nil
}

// A reserved prop name starts with two underscores followed by a non-underscore
func (c *checker) isReservedMemberName(name string) bool {
	return len(name) >= 1 && name[0] == '['
}

func (c *checker) getNamedMembers(members *binder.SymbolTable, meanings ... binder.SymbolFlags) []*binder.Symbol {
	var result []*binder.Symbol
	for _, symbol := range members.GetSymbols(meanings ... ) {
		if !c.isReservedMemberName(symbol.Name) {
			result = append(result, symbol)
		}
	}
	if result == nil {
		return c.emptySymbolArray
	} else {
		return result
	}
}

func (c *checker) setObjectTypeMembers(obj ObjectType, members *binder.SymbolTable,
	callSignatures []*Signature, constructSignatures []*Signature) ObjectType {
	obj.setMembers(members)
	obj.setFields(c.getNamedMembers(members, binder.FieldLike))
	obj.setMethods(c.getNamedMembers(members, binder.MethodLike))
	obj.setCallSignatures(callSignatures)
	obj.setConstructSignatures(constructSignatures)
	return obj
}

func (c *checker) createAnonymousType(symbol *binder.Symbol, members *binder.SymbolTable,
	callSignatures []*Signature, constructSignatures []*Signature) ObjectType {
	return c.setObjectTypeMembers(c.newObjectType(TFAnonymous, symbol),
		members, callSignatures, constructSignatures)
}

func (c *checker) isTypeAny(tpe Type) bool {
	return tpe != nil && tpe.Flags()&TFAny != 0
}

func (c *checker) getApparentType(tpe Type) Type {
	switch t := tpe.(type) {
	case *noBoundsType:
		return c.globalObjectType
	case *inBoundsType:
		return t.target
	case *outBoundsType:
		return t.target
	case *notNullType:
		return c.getApparentType(t.target)
	case *typeParameter:
		for true {
			tpe = c.getConstraintOfTypeParameter(tpe.(*typeParameter))
			if tpe == nil || tpe.Flags()&TFTypeParameter == 0 {
				break
			}
		}

		if isNull(tpe) || tpe == c.noConstraintType {
			tpe = c.globalObjectType
		}
	}

	return tpe
}

func (c *checker) getTypeOfVariableLikeDeclaration(declaration ast.VariableLike) Type {
	// Use type from type annotation if one is present
	if declaration.GetType() != nil {
		return c.getTypeFromTypeNode(declaration.GetType())
	}
	// A variable declared in a foreach statement
	if ast.IsForOfStatement(declaration.Parent()) {
		var stmt = declaration.Parent().(*ast.ForOfStatement)
		var exprType = c.checkExpression(stmt.Expression, nil)
		if c.isArrayTypeOrImplIterable(exprType) {
			var elementType = c.getElementTypeFromArrayTypeOrIterator(exprType)
			if elementType != nil {
				return elementType
			}
		}
		return c.unknownType
	}

	if ast.IsParameterDeclaration(declaration) {
		// Use contextual parameter type if one is available
		var tpe = c.getContextuallyTypedParameterType(declaration.(*ast.ParameterDeclaration))
		if tpe != nil {
			return tpe
		}
	}

	// Use the type of the initializer expression if one is present
	if declaration.GetInitializer() != nil {
		var initType = c.checkDeclarationInitializer(declaration)
		if initType.Flags()&TFClassLike != 0 &&
			initType.Symbol() != nil &&
			initType.Symbol().Flags&binder.AnonymousClass != 0 {
			// e.g
			// var object = new student(){}
			// object types is student
			var baseTypes = c.getBaseTypes(initType.(InterfaceType))
			if len(baseTypes) > 0 {
				return baseTypes[0].Type
			} else {
				// Error already report at 'getDeclaredTypeOfClassExpression'
				return c.unknownType
			}
		}

		if initType.Flags()&TFAnonymous != 0 {
			c.error(ast.GetDeclarationName(declaration), diagnostic.M_Unable_to_get_a_valid_type_from_the_anonymous_type)
		}
		return initType
	}

	return c.unknownType
}

func (c *checker) getTypeOfVariableOrParameterOrField(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.tpe == nil {
		var declaration = symbol.ValueDeclaration
		// Handle variable, parameter or property
		if !c.pushResolution(symbol, c.typeGetter) {
			return c.unknownType
		}
		var tpe = c.getTypeOfVariableLikeDeclaration(declaration.(ast.VariableLike))
		if !c.popResolution() {
			tpe = c.reportCircularityError(symbol)
		}
		links.tpe = tpe
	}
	return links.tpe
}

func (c *checker) getTypeOfFunctionOrMethod(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.tpe == nil {
		links.tpe = c.newObjectType(TFAnonymous, symbol)
	}
	return links.tpe
}

func (c *checker) getTypeOfClassLikeOrInterfaceLike(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.tpe == nil {
		links.tpe = c.newObjectType(TFAnonymous, symbol)
	}
	return links.tpe
}

func (c *checker) getTypeOfEnumMember(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.tpe == nil {
		links.tpe = c.getDeclaredTypeOfEnum(c.GetParentOfSymbol(symbol))
	}
	return links.tpe
}

func (c *checker) getTypeOfPackage(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.tpe == nil {
		links.tpe = c.newObjectType(TFAnonymous|TFPackage, symbol)
	}
	return links.tpe
}

func (c *checker) getTypeOfInstantiatedSymbol(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.tpe == nil {
		links.tpe = c.instantiateType(c.getTypeOfSymbol(links.target), links.mapper)
	}
	return links.tpe
}

func (c *checker) reportCircularityError(symbol *binder.Symbol) Type {
	// Check if variable has type annotation that circularly references the variable itself
	if symbol.ValueDeclaration.(ast.VariableLike).GetType() != nil {
		c.error(symbol.ValueDeclaration, diagnostic.M_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation,
			c.symbolToString(symbol, nil, 0, true))
	} else {
		c.error(symbol.ValueDeclaration, diagnostic.M_0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer,
			c.symbolToString(symbol, nil, 0, true))
	}
	return c.unknownType
}

func (c *checker) getTypeOfSymbol(symbol *binder.Symbol) Type {
	if c.getCheckFlags(symbol)&instantiated != 0 {
		return c.getTypeOfInstantiatedSymbol(symbol)
	}
	if symbol.Flags&(binder.Variable|binder.Field) != 0 {
		return c.getTypeOfVariableOrParameterOrField(symbol)
	}
	if symbol.Flags&(binder.Function|binder.Method) != 0 {
		return c.getTypeOfFunctionOrMethod(symbol)
	}
	if symbol.Flags&(binder.ClassLike|binder.InterfaceLike) != 0 {
		return c.getTypeOfClassLikeOrInterfaceLike(symbol)
	}
	if symbol.Flags &binder.EnumMember != 0 {
		return c.getTypeOfEnumMember(symbol)
	}
	if symbol.Flags&binder.AliasLike != 0 {
		return c.getTypeOfSymbol(c.resolveSymbol(symbol))
	}
	if symbol.Flags&binder.Package != 0 {
		return c.getTypeOfPackage(symbol)
	}
	return c.unknownType
}

func (c *checker) getTypeFromTypeArg(tpe *typeReference, index int) Type {
	if !isNull(tpe) && len(tpe.typeArguments) > 0 && index < len(tpe.typeArguments) {
		return tpe.typeArguments[index]
	}
	return nil
}

func (c *checker) getTargetType(tpe Type) Type {
	if tpe.hasFlags(TFReference) {
		return tpe.(TypeReference).Target()
	}
	return tpe
}

func (c *checker) hasBaseType(tpe Type, checkBase Type) bool {
	var check func(tpe Type) bool
	check = func(tpe Type) bool {
		if tpe.hasFlags(TFClass|TFInterface|TFReference) {
			var target = c.getTargetType(tpe).(InterfaceType)
			if target == checkBase {
				return true
			}

			for _, baseType := range c.getBaseTypes(target) {
				if check(baseType.Type) {
					return true
				}
			}
		}

		return false
	}

	return check(tpe)
}

func (c *checker) getBaseTypeWithTarget(tpe Type, findTarget ObjectType) Type {
	var find func(tpe Type) Type
	find = func(currentType Type) Type {
		if currentType.hasFlags(TFClass|TFInterface|TFReference) {
			var target = c.getTargetType(currentType).(InterfaceType)
			if target == findTarget {
				return currentType
			}

			var baseTypes []BaseType
			if currentType.hasFlags(TFClassLike|TFInterfaceLike) {
				baseTypes = c.getBaseTypes(currentType.(InterfaceType))
			} else if currentType.hasFlags(TFReference) {
				baseTypes = c.getBaseTypesOfTypeReference(currentType.(TypeReference))
			}
			for _, baseType := range baseTypes {
				if result := find(baseType.Type); !isNull(result) {
					return result
				}
			}
		}

		return nil
	}

	return find(tpe)
}

// Warning: The direct or concise'resolveName' inside this method may cause recursion illusory
func (c *checker) getBaseTypes(tpe InterfaceType) []BaseType {
	if tpe.baseTypes() == nil {
		if tpe.hasFlags(TFClass) {
			c.resolveBaseTypesOfClass(tpe)
		} else if tpe.hasFlags(TFAnonymousClass) {
			c.resolveBaseTypesOfAnonymousClass(tpe)
		} else if tpe.hasFlags(TFEnum) {
			c.resolveBaseTypesOfEnum(tpe)
		} else if tpe.hasFlags(TFInterface) {
			c.resolveBaseTypesOfInterface(tpe)
		} else if tpe.hasFlags(TFAnnotation) {
			c.resolveBaseTypesOfAnnotation(tpe)
		} else {
			panic(fmt.Sprintf("not support type(%T) in getBaseTypes", tpe))
		}
	}

	return tpe.baseTypes()
}

func (c *checker) getBaseTypesOfTypeReference(tpe TypeReference) []BaseType {
	if tpe.hasFlags(TFReference) && tpe.baseTypes() == nil {
		var target = tpe.Target()
		var mapper = c.createTypeMapper(toTypes(target.typeParams), tpe.TypeArguments())
		for _, baseType := range c.getBaseTypes(target) {
			var instantiatedBaseType = c.instantiateType(baseType.Type, mapper)
			tpe.addBaseType(BaseType{Type: instantiatedBaseType.(ObjectType), Kind: baseType.Kind})
		}

		// Set the mark to avoid repeated resolve
		if tpe.baseTypes() == nil {
			tpe.setBaseTypes(c.emptyBaseTypeArray)
		}
	}

	return tpe.baseTypes()
}

func (c *checker) resolveBaseTypesOfClass(declarationType InterfaceType) {
	var declaration = declarationType.Symbol().ValueDeclaration.(*ast.ClassDeclaration)
	if typeNode := ast.GetClassExtendsHeritageClauseElement(declaration); typeNode != nil {
		var baseType = c.getTypeFromTypeReferenceNode(typeNode)
		if baseType != c.unknownType {
			if c.getTargetType(baseType).Flags()&TFClass != 0 {
				c.checkTypeReferenceNode(typeNode)
				if declarationType != baseType && !c.hasBaseType(baseType, declarationType) {
					var baseTypeDeclaration = baseType.Symbol().ValueDeclaration
					if c.getDeclarationModifierFlagsFromDeclaration(baseTypeDeclaration)&ast.MFFinal != 0 {
						c.error(typeNode, diagnostic.M_Cannot_inherit_from_final_0,
							c.getSymbolAccessChain(baseType.Symbol(), true))
					}
					declarationType.addBaseType(BaseType{Type: baseType.(ObjectType), Kind: BTKExtends})
				} else {
					c.error(declaration, diagnostic.M_Type_0_recursively_references_itself_as_a_base_type,
						c.getSymbolAccessChain(declarationType.Symbol(), true))
				}
			} else {
				c.error(typeNode, diagnostic.M_A_class_may_only_extend_another_class)
			}
		}
	} else {
		// When getting the 'Object' type,'globalObjectType' is nil
		// So Object will not add itself as a base type
		assert(!isNull(c.globalObjectType))
		if declarationType != c.globalObjectType {
			declarationType.addBaseType(BaseType{Type: c.globalObjectType, Kind: BTKExtends})
		}
	}

	for _, implTypeNode := range ast.GetClassImplementsHeritageClauseElements(declaration) {
		var implType = c.getTypeFromTypeReferenceNode(implTypeNode)
		if implType != c.unknownType {
			if c.getTargetType(implType).Flags()&TFInterface != 0 {
				c.checkTypeReferenceNode(implTypeNode)
				curImpl := BaseType{Type: implType.(ObjectType), Kind: BTKImplement}
				if !declarationType.hasBaseType(curImpl) {
					declarationType.addBaseType(curImpl)
				} else {
					c.error(implTypeNode, diagnostic.M_Duplicate_implement_interface)
				}
			} else {
				c.error(implTypeNode, diagnostic.M_Interface_expected_here)
			}
		}
	}

	// Set the mark to avoid repeated resolve
	if declarationType.baseTypes() == nil {
		declarationType.setBaseTypes(c.emptyBaseTypeArray)
	}
}

func (c *checker) resolveBaseTypesOfAnonymousClass(declarationType InterfaceType) {
	var declaration = declarationType.Symbol().ValueDeclaration.(*ast.BodyDeclaration)
	var newObjectExpression = declaration.Parent().(*ast.NewObjectExpression)
	var baseType = c.checkExpression(newObjectExpression.Type, nil)
	if baseType != c.unknownType {
		if c.getTargetType(baseType).Flags()&(TFClassLike|TFInterfaceLike) != 0 {
			var baseTypeKind BaseTypeKind
			if c.getTargetType(baseType).Flags()&TFClassLike != 0 {
				var baseTypeDeclaration = c.getTargetType(baseType).Symbol().ValueDeclaration
				if c.getDeclarationModifierFlagsFromDeclaration(baseTypeDeclaration)&ast.MFFinal != 0 {
					c.error(declaration, diagnostic.M_Cannot_inherit_from_final_0,
						c.getSymbolAccessChain(baseType.Symbol(), true))
				}

				baseTypeKind = BTKExtends
			} else {
				baseTypeKind = BTKImplement
			}
			declarationType.addBaseType(BaseType{Type: baseType.(ObjectType), Kind: baseTypeKind})
		} else {
			c.error(newObjectExpression.Type, diagnostic.M_Anonymous_class_only_extends_class_or_implement_interface)
		}
	} else {
		// For better error prompts and language services
		if !isNull(c.globalObjectType) {
			declarationType.addBaseType(BaseType{Type: c.globalObjectType, Kind: BTKExtends})
		}
	}

	// Set the mark to avoid repeated resolve
	if declarationType.baseTypes() == nil {
		declarationType.setBaseTypes(c.emptyBaseTypeArray)
	}
}

func (c *checker) resolveBaseTypesOfEnum(declarationType InterfaceType) {
	// Because enumeration cannot be inherited, we directly add
	declarationType.addBaseType(BaseType{Type: c.globalObjectType, Kind: BTKExtends})

	var declaration = declarationType.Symbol().ValueDeclaration.(*ast.EnumDeclaration)
	for _, implTypeNode := range ast.GetClassImplementsHeritageClauseElements(declaration) {
		var implType = c.getTypeFromTypeReferenceNode(implTypeNode)
		if implType != c.unknownType {
			if c.getTargetType(implType).Flags()&TFInterface != 0 {
				curImpl := BaseType{Type: implType.(ObjectType), Kind: BTKImplement}
				if !declarationType.hasBaseType(curImpl) {
					declarationType.addBaseType(curImpl)
				} else {
					c.error(implTypeNode, diagnostic.M_Duplicate_implement_interface)
				}
			} else {
				c.error(implTypeNode, diagnostic.M_Interface_expected_here)
			}
		}
	}
}

func (c *checker) resolveBaseTypesOfInterface(declarationType InterfaceType) {
	var declaration = declarationType.Symbol().ValueDeclaration.(*ast.InterfaceDeclaration)
	var extends = ast.GetInterfaceExtendsHeritageClauseElement(declaration)
	if extends != nil {
		for _, node := range extends {
			var baseType = c.getTypeFromTypeReferenceNode(node)
			if baseType != c.unknownType {
				if c.getTargetType(baseType).Flags()&(TFInterface) != 0 {
					if declarationType != baseType && !c.hasBaseType(baseType.(ObjectType), declarationType) {
						declarationType.addBaseType(BaseType{Type: baseType.(ObjectType), Kind: BTKExtends})
					} else {
						c.error(declaration, diagnostic.M_Type_0_recursively_references_itself_as_a_base_type,
							c.getSymbolAccessChain(declarationType.Symbol(),true))
					}
				} else {
					c.error(node, diagnostic.M_Interface_expected_here)
				}
			}
		}
	} else {
		// The interface also inherits Object by default, can proceed  override check.
		if !isNull(c.globalObjectType) {
			declarationType.addBaseType(BaseType{Type: c.globalObjectType, Kind: BTKExtends})
		}
	}

	// Set the mark to avoid repeated resolve
	if declarationType.baseTypes() == nil {
		declarationType.setBaseTypes(c.emptyBaseTypeArray)
	}
}

func (c *checker) resolveBaseTypesOfAnnotation(declarationType InterfaceType) {
	assert(!isNull(c.globalAnnotationType))

	// Annotations are inherited from 'Annotation' interface by default
	declarationType.addBaseType(BaseType{Type: c.globalAnnotationType, Kind: BTKExtends})
}

func (c *checker) getClassExtendsType(tpe InterfaceType) ObjectType {
	for _, baseType := range c.getBaseTypes(tpe) {
		if baseType.Kind == BTKExtends {
			return baseType.Type.(ObjectType)
		}
	}
	return nil
}

// Get the type parameters given by a list of declarations to a set of type parameters and returns the resulting set.
// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set
// in-place and returns the same array.
func (c *checker) getTypeParametersFromDeclarations(declarations []*ast.TypeParameterDeclaration) []*typeParameter {
	var result []*typeParameter
	for _, declaration := range declarations {
		var type_ = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfNode(declaration))
		result = append(result, type_)
	}
	return result
}

// Return combined list of type parameters from all declarations of a class or interface. Elsewhere we check they're all
// the same, but even if they're not we still need the complete list to ensure instantiations supply type arguments
// for all type parameters.
func (c *checker) getTypeParametersOfClassOrInterface(symbol *binder.Symbol) []*typeParameter {
	if len(symbol.Declarations) == 0 {
		return nil
	}

	var node = symbol.Declarations[0]
	var result []*typeParameter

	var typeParams *ast.DeclarationList
	switch n := node.(type) {
	case *ast.InterfaceDeclaration:
		typeParams = n.TypeParameters
	case *ast.ClassDeclaration:
		typeParams = n.TypeParameters
	default:
		return nil
	}

	if typeParams != nil {
		for i := 0; i < typeParams.Len(); i ++ {
			var typeParam = typeParams.At(i)
			var symbol = binder.GetSymbolOfNode(typeParam)
			var type_ = c.getDeclaredTypeOfTypeParameter(symbol)
			result = append(result, type_)
		}
	}

	return result
}

func (c *checker) getDeclaredTypeOfClass(symbol *binder.Symbol) InterfaceType {
	var links = c.getSymbolLinks(symbol)
	if links.declarationType == nil {
		var declarationType InterfaceType
		var typeParams = c.getTypeParametersOfClassOrInterface(symbol)
		if typeParams != nil {
			var generic = c.newGenericType(TFClass|TFReference, symbol)
			generic.typeParams = typeParams
			generic.instantiations = make(map[string]TypeReference)
			generic.instantiations[c.getTypeListId(toTypes(generic.typeParams))] = generic
			generic.target = generic
			generic.typeArgs = toTypes(typeParams)
			declarationType = generic
		} else {
			declarationType = c.newInterfaceType(TFClass, symbol)
		}

		var declaredMembers = binder.NewSymbolTableWithSymbols(c.getNamedMembers(symbol.Members))
		declarationType.setDeclaredMembers(declaredMembers)

		links.declarationType = declarationType
	}
	return links.declarationType.(InterfaceType)
}

func (c *checker) getDeclaredTypeOfAnonymousClass(symbol *binder.Symbol) InterfaceType {
	var links = c.getSymbolLinks(symbol)
	if links.declarationType == nil {
		var declType = c.newInterfaceType(TFAnonymousClass, symbol)
		links.declarationType = declType

		var declaredMembers = binder.NewSymbolTableWithSymbols(c.getNamedMembers(symbol.Members))
		declType.setDeclaredMembers(declaredMembers)
	}
	return links.declarationType.(InterfaceType)
}

func (c *checker) getDeclaredTypeOfInterface(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.declarationType == nil {
		var declarationType InterfaceType
		var typeParameters = c.getTypeParametersOfClassOrInterface(symbol)
		if typeParameters != nil {
			var generic = c.newGenericType(TFInterface|TFReference, symbol)
			generic.typeParams = typeParameters
			generic.instantiations = make(map[string]TypeReference)
			generic.instantiations[c.getTypeListId(toTypes(generic.typeParams))] = generic
			generic.target = generic
			generic.typeArgs = toTypes(typeParameters)
			declarationType = generic
		} else {
			declarationType = c.newInterfaceType(TFInterface, symbol)
		}

		var declaredMembers = binder.NewSymbolTableWithSymbols(c.getNamedMembers(symbol.Members))
		declarationType.setDeclaredMembers(declaredMembers)
		links.declarationType = declarationType
	}
	return links.declarationType
}

func (c *checker) getDeclaredTypeOfEnum(symbol *binder.Symbol) Type {
	var links = c.getSymbolLinks(symbol)
	if links.declarationType == nil {
		var declarationType = c.newInterfaceType(TFEnum, symbol)
		var declaredMembers = binder.NewSymbolTableWithSymbols(c.getNamedMembers(symbol.Members))
		declarationType.setDeclaredMembers(declaredMembers)
		links.declarationType = declarationType
	}
	return links.declarationType
}

func (c *checker) getDeclaredTypeOfAnnotation(symbol *binder.Symbol) *interfaceType {
	var links = c.getSymbolLinks(symbol)
	if links.declarationType == nil {
		var declarationType = c.newInterfaceType(TFAnnotation, symbol)

		var declaredMembers = binder.NewSymbolTableWithSymbols(c.getNamedMembers(symbol.Members))
		declarationType.setDeclaredMembers(declaredMembers)
		links.declarationType = declarationType
	}
	return links.declarationType.(*interfaceType)
}

func (c *checker) getDeclaredTypeOfTypeParameter(symbol *binder.Symbol) *typeParameter {
	var links = c.getSymbolLinks(symbol)
	if links.declarationType == nil {
		var tpe = c.newTypeParam(TFTypeParameter, symbol)
		if symbol.Declarations[0].(*ast.TypeParameterDeclaration).Constraint == nil {
			tpe.constraint = c.noConstraintType
		}
		links.declarationType = tpe
	}
	return links.declarationType.(*typeParameter)
}

func (c *checker) getDeclaredTypeOfSymbol(symbol *binder.Symbol) Type {
	if symbol.Flags&binder.Class != 0 {
		return c.getDeclaredTypeOfClass(symbol)
	}
	if symbol.Flags&binder.AnonymousClass != 0 {
		return c.getDeclaredTypeOfAnonymousClass(symbol)
	}
	if symbol.Flags&binder.Interface != 0 {
		return c.getDeclaredTypeOfInterface(symbol)
	}
	if symbol.Flags&binder.Annotation != 0 {
		return c.getDeclaredTypeOfAnnotation(symbol)
	}
	if symbol.Flags&binder.Enum != 0 {
		return c.getDeclaredTypeOfEnum(symbol)
	}
	if symbol.Flags&binder.TypeParameter != 0 {
		return c.getDeclaredTypeOfTypeParameter(symbol)
	}
	if symbol.Flags&binder.AliasLike != 0 {
		return c.getDeclaredTypeOfSymbol(c.resolveSymbol(symbol))
	}
	if c.getCheckFlags(symbol)&instantiated != 0 {
		panic("Should never get an instantiated symbol here.")
	}
	return c.unknownType
}

func (c *checker) createSymbolTable(symbols []*binder.Symbol) *binder.SymbolTable {
	var result = binder.NewSymbolTable()
	for i := 0; i < len(symbols); i++ {
		var symbol = symbols[i]
		result.Table[symbol.Name] = append(result.Table[symbol.Name], symbol)
	}
	return result
}

func (c *checker) createInstantiatedSymbolTable(symbols []*binder.Symbol, mapper *TypeMapper) *binder.SymbolTable {
	var result = binder.NewSymbolTable()
	for i := 0; i < len(symbols); i++ {
		var symbol = symbols[i]
		result.AppendSymbol(symbol.Name, c.instantiateSymbol(symbol, mapper))
	}
	return result
}

type inheritedMode = int

const (
	imImplement = 1 << iota
	imExtends
	imAbstract
)

func (c *checker) addInheritedMembers(parent *binder.Symbol, baseParent *binder.Symbol, symbols *binder.SymbolTable, baseSymbols []*binder.Symbol, mode inheritedMode) {
	var aliasSymbols []*binder.Symbol
	for _, baseSymbol := range baseSymbols {
		var symbol = symbols.GetSymbol(baseSymbol.Name, baseSymbol.Flags&(^binder.Transient))
		if symbol == nil || baseSymbol.Flags&binder.AliasLike != 0 {
			if baseSymbol.Flags&binder.AliasLike != 0 {
				aliasSymbols = append(aliasSymbols, baseSymbol)
				continue
			}

			if baseSymbol.Flags&binder.Method != 0 && mode&imImplement != 0{
				var missingSymbol = newMissingMethodSymbol(parent, baseSymbol.Name)
				symbols.AppendSymbol(missingSymbol.Name, missingSymbol)
				continue
			}

			if baseSymbol.Flags&binder.Method == 0 && c.getDeclarationModifierFlagsFromSymbol(baseSymbol)&ast.MFPrivate != 0 {
				// Private members cannot be inherited,Because there are multiple
				// method declarations, it cannot be judged directly by symbols
				continue
			}

			symbols.AppendSymbol(baseSymbol.Name, baseSymbol)
		}
	}

	// Redirection alias symbol
	for _, aliasSymbol := range aliasSymbols {
		var mappedName = c.getAliasSymbolMappedName(aliasSymbol)
		var meaning binder.SymbolFlags
		if aliasSymbol.Flags&binder.FieldAlias != 0 {
			meaning = binder.Field
		} else if aliasSymbol.Flags&binder.MethodAlias != 0 {
			meaning = binder.Method
		}

		var mappedSymbol = symbols.GetSymbol(mappedName, meaning)
		// No need to redirect with the original mapping
		if c.resolveSymbol(aliasSymbol) == mappedSymbol {
			symbols.AppendSymbol(aliasSymbol.Name, aliasSymbol)
			continue
		}

		var resolve = c.newAliasSymbol(aliasSymbol, mappedSymbol)
		symbols.AppendSymbol(aliasSymbol.Name, resolve)
	}

	c.addInheritedSignatures(parent, baseParent, symbols, baseSymbols, mode)
}

func (c *checker) isAbstractSignature(signature *Signature) bool {
	if signature.Declaration == nil {
		return false
	}

	return c.getDeclarationModifierFlagsFromDeclaration(signature.Declaration)&ast.MFAbstract != 0
}

func (c *checker) isSignatureFromInterfaceLike(signature *Signature) bool {
	if signature.Declaration == nil {
		return false
	}

	return ast.IsInterfaceLike(signature.Declaration.Parent())
}

func (c *checker) isMissingImplementSignature(parent, symbol *binder.Symbol, signature *Signature) bool {
	var link = c.getSymbolLinks(parent)
	for _, missingSignature := range link.missingImplementSignatures[symbol] {
		if missingSignature == signature {
			return true
		}
	}
	return false
}

func (c *checker) replaceMissingSignature(parent, symbol *binder.Symbol, oldSignature *Signature, newSignature *Signature) bool {
	var link = c.getSymbolLinks(parent)
	for i, missingSignature := range link.missingImplementSignatures[symbol] {
		if missingSignature == oldSignature {
			link.missingImplementSignatures[symbol][i] = newSignature
			return true
		}
	}
	return false
}

func (c *checker) addInheritedSignatures(parent, baseParent *binder.Symbol, symbols *binder.SymbolTable, baseSymbols []*binder.Symbol, mode inheritedMode) {
	var parentLinks = c.getSymbolLinks(parent)
	var baseParentLinks = c.getSymbolLinks(baseParent)

	for _, baseSymbol := range baseSymbols {
		if baseSymbol.Flags&binder.Method == 0 {
			continue
		}

		var symbol = symbols.GetSymbol(baseSymbol.Name, binder.Method)
		if symbol == baseSymbol {
			// Extends inherited signatures
			if mode&imExtends != 0 {
				parentLinks.inheritedSignatures[symbol] =
					append(parentLinks.inheritedSignatures[symbol],
						baseParentLinks.inheritedSignatures[baseSymbol]...)
			}

			// Extends missing implement signatures
			if mode&imExtends != 0 && mode&imAbstract != 0 {
				parentLinks.missingImplementSignatures[symbol] =
					append(parentLinks.missingImplementSignatures[symbol],
						baseParentLinks.missingImplementSignatures[baseSymbol]...)
			}

			continue
		}

	outer:
		for _, baseSignature := range baseParentLinks.inheritedSignatures[baseSymbol] {
			// Private members cannot be inherited
			if mode&imExtends != 0 && c.getDeclarationModifierFlagsFromDeclaration(baseSignature.Declaration)&ast.MFPrivate != 0 {
				continue
			}

			if mode&imImplement != 0 {
				// In the implementation mode, static methods do not need to be implemented
				if c.getDeclarationModifierFlagsFromDeclaration(baseSignature.Declaration)&ast.MFStatic != 0 {
					continue
				}
			}

			for _, signature := range parentLinks.inheritedSignatures[symbol] {
				if c.isSignatureParametersTypeIdentical(signature, baseSignature) {
					// If in'imImplement' mode, It may be possible to replace the existing missing signature
					// Because the return value type of the missing signature may not be best
					if mode&imImplement != 0 {
						// Signatures that are not missing cannot be replaced
						if !c.isMissingImplementSignature(parent, symbol, signature) {
							continue outer
						}

						// default signature can't replace abstract signature
						if c.isAbstractSignature(signature) &&
							!c.isAbstractSignature(baseSignature) {
							continue outer
						}

						// 1. abstract signature replace abstract signature
						// 2. abstract signature replace default signature
						if c.isSignatureOverrideTo(baseSignature, signature) {
							c.replaceMissingSignature(parent, symbol, signature, baseSignature)
							continue outer
						}
					}

					continue outer
				}
			}

			parentLinks.inheritedSignatures[symbol] = append(parentLinks.inheritedSignatures[symbol], baseSignature)
			if mode&(imAbstract|imImplement) != 0 && c.isMissingImplementSignature(baseParent, baseSymbol, baseSignature) {
				parentLinks.missingImplementSignatures[symbol] = append(parentLinks.missingImplementSignatures[symbol], baseSignature)
			}
		}
	}
}

func (c *checker) containerSignature(arr []*Signature, signature *Signature) bool {
	for _, item := range arr {
		if item == signature {
			return true
		}
	}
	return false
}

func (c *checker) initializationInheritedSignatures(parent *binder.Symbol, members *binder.SymbolTable) {
	var parentLinks = c.getSymbolLinks(parent)
	if parentLinks.inheritedSignatures == nil {
		parentLinks.inheritedSignatures = make(map[*binder.Symbol][]*Signature)
	}
	if parentLinks.missingImplementSignatures == nil {
		parentLinks.missingImplementSignatures = make(map[*binder.Symbol][]*Signature)
	}

	for _, symbol := range c.getNamedMembers(members, binder.Method) {
		var signatures []*Signature
		if c.getCheckFlags(symbol)&instantiated != 0 {
			var tpe = c.getTypeOfInstantiatedSymbol(symbol)
			signatures = tpe.(ObjectType).CallSignatures()
		} else {
			signatures = c.getSignaturesOfSymbol(symbol)
		}

		for _, signature := range signatures {
			if !c.containerSignature(parentLinks.inheritedSignatures[symbol], signature) {
				parentLinks.inheritedSignatures[symbol] = append(parentLinks.inheritedSignatures[symbol], signature)
			}

			if c.isAbstractSignature(signature) && !c.containerSignature(parentLinks.missingImplementSignatures[symbol], signature){
				parentLinks.missingImplementSignatures[symbol] = append(parentLinks.missingImplementSignatures[symbol], signature)
			}
		}
	}
}

func (c *checker) getInheritedMode(tpe InterfaceType, base ObjectType) inheritedMode {
	for _, baseType := range c.getBaseTypes(tpe) {
		if baseType.Type == base {
			if baseType.Kind == BTKExtends {
				if tpe.Flags()&TFClassLike != 0 {
					if base.Symbol() != nil && base.Symbol().ValueDeclaration != nil &&
						ast.GetModifierFlags(base.Symbol().ValueDeclaration)&ast.MFAbstract != 0 {
						return imExtends|imAbstract
					}
				}
				return imExtends
			} else {
				return imImplement
			}
		}
	}

	panic("base type not int base type list in 'getInheritedMode'")
}

func (c *checker) resolveClassLikeOrInterfaceLikeMembers(tpe InterfaceType) {
	var members = tpe.Symbol().Members
	c.initializationInheritedSignatures(tpe.Symbol(), members)
	if len(c.getBaseTypes(tpe)) > 0 {
		members = c.createSymbolTable(c.getNamedMembers(tpe.Symbol().Members,
			binder.Field|binder.Method|binder.Class|binder.AliasLike))
		for _, baseType := range c.getBaseTypes(tpe) {
			c.addInheritedMembers(tpe.Symbol(), baseType.Type.Symbol(), members, c.getMembersOfType(baseType.Type,
				binder.Field|binder.Method|binder.Class|binder.AliasLike), c.getInheritedMode(tpe, baseType.Type))
		}
	}
	c.setObjectTypeMembers(tpe, members, nil, nil)
}

func (c *checker) resolveTypeReferenceMembers(tpe *typeReference) {
	var target = tpe.target
	var mapper = c.createTypeMapper(toTypes(target.typeParams), tpe.typeArguments)
	var members = c.createInstantiatedSymbolTable(target.declaredMembers.GetAll(), mapper)
	c.initializationInheritedSignatures(tpe.symbol, members)
	for i, baseType := range c.getBaseTypesOfTypeReference(tpe) {
		c.addInheritedMembers(tpe.symbol, baseType.Type.Symbol(), members, c.getMembersOfType(baseType.Type,
			binder.Field|binder.Method|binder.Class|binder.AliasLike), c.getInheritedMode(target, c.getBaseTypes(target)[i].Type))
	}
	c.setObjectTypeMembers(tpe, members, nil, nil)
}

func (c *checker) createSignature(decl ast.Declaration, typeParams []*typeParameter,
	params []*binder.Symbol, throws []Type, resolvedReturnType Type, minArgumentCount int, hasRestParameter bool) *Signature {
	var signature = new(Signature)
	signature.Declaration = decl
	// Deep copy
	if typeParams != nil {
		signature.TypeParameters = make([]*typeParameter, len(typeParams))
		copy(signature.TypeParameters, typeParams)
	}
	if params != nil {
		signature.Parameters = make([]*binder.Symbol, len(params))
		copy(signature.Parameters, params)
	}
	if throws != nil {
		signature.Throws = make([]Type, len(throws))
		copy(signature.Throws, throws)
	}
	signature.ResolvedReturnType = resolvedReturnType
	signature.MinArgumentCount = minArgumentCount
	signature.HasRestParameter = hasRestParameter
	return signature
}

func (c *checker) cloneSignature(sig *Signature) *Signature {
	return c.createSignature(sig.Declaration, sig.TypeParameters, sig.Parameters, sig.Throws,
		sig.ResolvedReturnType, sig.MinArgumentCount, sig.HasRestParameter)
}

func (c *checker) getDefaultConstructSignatures(classType InterfaceType) []*Signature {
	return []*Signature{c.createSignature(nil, classType.TypeParameters(),
		c.emptySymbolArray, nil, classType, 0, false)}
}

func (c *checker) resolveAnonymousTypeMembers(tpe ObjectType) {
	var symbol = tpe.Symbol()
	var members  = c.emptySymbols
	var callSignatures= c.emptySignatureArray
	var constructSignatures = c.emptySignatureArray

	if symbol.Flags&binder.HasExports != 0 {
		members = symbol.Exports
	}
	if symbol.Flags&binder.Function != 0 {
		callSignatures = c.getSignaturesOfSymbol(symbol)
	}
	if symbol.Flags&binder.Method != 0 {
		c.resolveObjectTypeMembers(c.getTypeOfSymbol(symbol.Parent).(ObjectType))
		c.resolveObjectTypeMembers(c.getDeclaredTypeOfSymbol(symbol.Parent).(ObjectType))
		callSignatures = c.getInheritedSignaturesOfSymbol(symbol)
	}
	if symbol.Flags&binder.ClassLike != 0 {
		var classType = c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)
		constructSignatures = c.getSignaturesOfSymbol(symbol.Members.GetSymbol(ast.ConstructorName, binder.Constructor))
		if len(constructSignatures) == 0 {
			constructSignatures = c.getDefaultConstructSignatures(classType)
		}
	}
	if symbol.Flags&(binder.ClassLike|binder.InterfaceLike) != 0 {
		var declType = c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)
		var baseType = c.getClassExtendsType(declType)
		c.initializationInheritedSignatures(tpe.Symbol(), members)
		if baseType != nil {
			var staticBaseType = c.getTypeOfSymbol(baseType.Symbol())
			members = c.createSymbolTable(c.getNamedMembers(members))
			c.addInheritedMembers(tpe.Symbol(), staticBaseType.Symbol(), members,
				c.getMembersOfType(staticBaseType, binder.FieldLike|binder.MethodLike|binder.Class), imExtends)

		}
	}

	c.setObjectTypeMembers(tpe, members, callSignatures, constructSignatures)
}

func (c *checker) resolveObjectTypeMembers(tpe ObjectType) ObjectType {
	if tpe.Members() == nil {
		if tpe.Flags()&(TFClassLike|TFInterfaceLike) != 0 {
			c.resolveClassLikeOrInterfaceLikeMembers(tpe.(InterfaceType))
		} else if tpe.Flags()&TFAnonymous != 0 {
			c.resolveAnonymousTypeMembers(tpe)
		} else {
			c.resolveTypeReferenceMembers(tpe.(*typeReference))
		}
	}
	return tpe
}

func (c *checker) getMembersOfType(t Type, meanings ... binder.SymbolFlags) []*binder.Symbol {
	if t.Flags()&TFObjectLike != 0 {
		return c.getNamedMembers(c.resolveObjectTypeMembers(t.(ObjectType)).Members(), meanings...)
	}
	return c.emptySymbolArray
}

func (c *checker) getFieldsOfType(t Type) []*binder.Symbol {
	if t.Flags()&TFObjectLike != 0 {
		return c.resolveObjectTypeMembers(t.(ObjectType)).Fields()
	}
	return c.emptySymbolArray
}

func (c *checker) getMethodsOfType(t Type) []*binder.Symbol {
	if t.Flags()&TFObjectLike != 0 {
		return c.resolveObjectTypeMembers(t.(ObjectType)).Methods()
	}
	return c.emptySymbolArray
}

// Get the inherited signature from the parent symbol
func (c *checker) getInheritedSignaturesOfSymbol(symbol *binder.Symbol) []*Signature{
	if symbol.Flags&binder.Method != 0 && symbol.Parent != nil {
		var links = c.getSymbolLinks(symbol.Parent)
		return links.inheritedSignatures[symbol]
	}
	return nil
}

func (c *checker) getFieldOfType(t Type, name string) *binder.Symbol {
	return c.getMemberOfType(t, name, binder.FieldLike)
}

func (c *checker) getMethodOfType(t Type, name string) *binder.Symbol {
	return c.getMemberOfType(t, name, binder.MethodLike)
}

func (c *checker) getMethodSignatureOfType(t Type, name string) []*Signature {
	var symbol = c.getMemberOfType(t, name, binder.Method)
	return c.getInheritedSignaturesOfSymbol(symbol)
}

func (c *checker) getMemberOfType(t Type, name string, meanings ... binder.SymbolFlags) *binder.Symbol {
	if t.Flags()&TFObjectLike != 0 {
		var resolved = c.resolveObjectTypeMembers(t.(ObjectType))
		if symbol := resolved.Members().GetSymbol(name, meanings ...); symbol != nil {
			return symbol
		}
	}
	return nil
}

func (c *checker) getSignaturesOfType(t Type, kind SignatureKind) []*Signature {
	if t.Flags()&(TFObjectLike|TFAnonymous)!= 0 {
		var resolved = c.resolveObjectTypeMembers(t.(ObjectType))
		if kind == SIKCall {
			return resolved.CallSignatures()
		} else {
			return resolved.ConstructSignatures()
		}
	}
	return c.emptySignatureArray
}

func containsTypeParameter(arr []*typeParameter, item *typeParameter) bool {
	for _, param := range arr {
		if param == item {
			return true
		}
	}
	return false
}

// Return list of type parameters with duplicates removed (duplicate identifier errors are generated in the actual
// type checking functions).
func (c *checker) getTypeParametersFromDeclaration(typeParams *ast.DeclarationList) []*typeParameter {
	var result []*typeParameter
	for i := 0; i < typeParams.Len(); i ++ {
		var node = typeParams.At(i)
		var tpe = c.getDeclaredTypeOfTypeParameter(binder.GetSymbolOfNode(node))
		if !containsTypeParameter(result, tpe) {
			result = append(result, tpe)
		}
	}
	return result
}

func (c *checker) isUnknownSignature(signature *Signature) bool {
	return signature == c.unknownSignature
}

func (c *checker) getSignatureFromDeclaration(decl ast.FunctionLike) *Signature {
	var links = c.getNodeLinks(decl)
	if links.resolvedSignature == nil {
		var classType InterfaceType
		if ast.IsConstructorDeclaration(decl) {
			classType = c.getDeclaredTypeOfClass(binder.GetSymbolOfNode(decl.Parent()))
		}
		var declTypeParams = decl.GetTypeParameters()
		var typeParameters []*typeParameter
		if classType != nil {
			typeParameters = classType.TypeParameters()
		} else if declTypeParams != nil {
			typeParameters = c.getTypeParametersFromDeclaration(declTypeParams)
		}
		var throws []Type
		var declThrows = decl.GetThrows()
		for i := 0; i < declThrows.Len(); i ++ {
			throws = append(throws, c.getTypeFromTypeNode(declThrows.At(i)))
		}
		var parameters []*binder.Symbol
		var minArgumentCount = -1
		var declParams = decl.GetParameters()
		for i := 0; i < declParams.Len(); i ++ {
			var param = declParams.At(i).(*ast.ParameterDeclaration)
			parameters = append(parameters, binder.GetSymbolOfNode(param))
			if minArgumentCount < 0 {
				if param.Initializer != nil || param.Dots != nil {
					minArgumentCount = i
				}
			}
		}

		if minArgumentCount < 0 {
			minArgumentCount = declParams.Len()
		}

		var declResult = decl.GetType()

		var returnType Type
		if classType != nil {
			returnType = classType
		} else if declResult != nil {
			returnType = c.getTypeFromTypeNode(declResult)
		} else if !ast.IsArrowFunctionDeclaration(decl) {
			returnType = c.voidType
		}

		links.resolvedSignature = c.createSignature(decl, typeParameters,
			parameters, throws, returnType, minArgumentCount, ast.HasRestParameter(decl))
	}
	return links.resolvedSignature
}

func (c *checker) getSignaturesOfSymbol(symbol *binder.Symbol) []*Signature {
	if symbol == nil {
		return c.emptySignatureArray
	}
	var result []*Signature
	for i := 0; i < len(symbol.Declarations); i++ {
		var node = symbol.Declarations[i]
		switch n := node.(type) {
		case *ast.ConstructorDeclaration:
			result = append(result, c.getSignatureFromDeclaration(n))
		case *ast.MethodDeclaration:
			result = append(result, c.getSignatureFromDeclaration(n))
		case *ast.ArrowFunctionDeclaration:
			result = append(result, c.getSignatureFromDeclaration(n))
		}
	}
	return result
}

func (c *checker) getReturnTypeOfSignature(signature *Signature) Type {
	if signature.ResolvedReturnType == nil {
		if !c.pushResolution(signature, c.signatureResolvedReturnTypeGetter) {
			return c.unknownType
		}

		var tpe Type
		if signature.Target != nil {
			tpe = c.instantiateType(c.getReturnTypeOfSignature(signature.Target), signature.Mapper)
		} else {
			tpe = c.getReturnTypeFromBody(signature.Declaration, nil)
		}
		if !c.popResolution() {
			// Report error, can not resolve return type.
			var decl = signature.Declaration
			var name = ast.GetDeclarationName(decl)
			if name != nil {
				c.error(name, diagnostic.M_Unable_to_resolve_return_type_of_0,
					ast.DeclarationNameToString(name))
			} else {
				c.error(decl, diagnostic.M_Unable_to_resolve_return_type)
			}

			tpe = c.unknownType
		}
		signature.ResolvedReturnType = tpe
	}
	return signature.ResolvedReturnType
}

func (c *checker) getRestTypeOfSignature(signature *Signature) Type {
	if signature.HasRestParameter {
		var tpe = c.getTypeOfSymbol(signature.Parameters[len(signature.Parameters)-1])
		if tpe.Flags()&TFReference != 0 && tpe.(*typeReference).target == c.globalArrayType {
			return tpe.(*typeReference).typeArguments[0]
		}
	}
	return c.unknownType
}

func (c *checker) getSignatureInstantiation(signature *Signature, typeArguments []Type) *Signature {
	return c.instantiateSignature(signature, c.createTypeMapper(toTypes(signature.TypeParameters), typeArguments), true)
}

func (c *checker) getErasedSignature(signature *Signature) *Signature {
	if isNull(signature.TypeParameters) {
		return signature
	}
	if isNull(signature.ErasedSignatureCache) {
		if !isNull(signature.Target) {
			signature.ErasedSignatureCache = c.instantiateSignature(c.getErasedSignature(signature.Target), signature.Mapper, false)
		} else {
			signature.ErasedSignatureCache = c.instantiateSignature(signature, c.createTypeEraser(toTypes(signature.TypeParameters)), true)
		}
	}
	return signature.ErasedSignatureCache
}

func (c *checker) getOrCreateTypeFromSignature(signature *Signature) ObjectType {
	if signature.IsolatedSignatureType == nil {
		var isConstructor = ast.IsConstructorDeclaration(signature.Declaration)
		var tpe = c.newObjectType(TFAnonymous, nil)
		tpe.members = c.emptySymbols
		tpe.fields = c.emptySymbolArray
		if !isConstructor {
			tpe.callSignatures = []*Signature{signature}
		} else {
			tpe.callSignatures = c.emptySignatureArray
		}
		if isConstructor {
			tpe.constructSignatures = []*Signature{signature}
		} else {
			tpe.constructSignatures = c.emptySignatureArray
		}
		signature.IsolatedSignatureType = tpe
	}

	return signature.IsolatedSignatureType
}

func (c *checker) getConstraintDeclaration(t *typeParameter) ast.Type {
	return t.symbol.Declarations[0].(*ast.TypeParameterDeclaration).Constraint
}

func (c *checker) getConstraintOfTypeParameter(tpe *typeParameter) Type {
	if tpe.constraint == nil {
		if tpe.target != nil {
			var targetConstraint = c.getConstraintOfTypeParameter(tpe.target)
			if targetConstraint != nil {
				return c.instantiateType(targetConstraint, tpe.mapper)
			} else {
				tpe.constraint = c.noConstraintType
			}
		} else {
			var constraintDeclaration = c.getConstraintDeclaration(tpe)
			if constraintDeclaration != nil {
				return c.getTypeFromTypeNode(constraintDeclaration)
			} else {
				tpe.constraint = c.noConstraintType
			}
		}
	}

	if tpe.constraint != c.noConstraintType {
		return tpe.constraint
	}
	return nil
}

func (c *checker) getTypeListId(types []Type) string {
	switch len(types) {
	case 1:
		return strconv.Itoa(types[0].ID())
	case 2:
		return strconv.Itoa(types[0].ID()) + "," + strconv.Itoa(types[1].ID())
	default:
		var result = ""
		for i := 0; i < len(types); i++ {
			if i > 0 {
				result += ","
			}
			result += strconv.Itoa(types[i].ID())
		}
		return result
	}
}

func (c *checker) createTypeReference(target *genericType, typeArguments []Type) TypeReference {
	var id = c.getTypeListId(typeArguments)
	var tpe = target.instantiations[id]
	if tpe == nil {
		var ref = c.newTypeReferencer(TFReference, target.symbol)
		ref.target = target
		ref.typeArguments = typeArguments
		target.instantiations[id] = ref
		return ref
	}
	return tpe
}

// Get type from reference to class or interface
func (c *checker) getTypeFromClassOrInterfaceReference(node *ast.TypeReference, symbol *binder.Symbol) Type {
	var tpe = c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)
	var typeParameters = tpe.TypeParameters()
	if typeParameters != nil {
		if node.TypeArguments == nil || node.TypeArguments.Len() != len(typeParameters) {
			c.error(node, diagnostic.M_Generic_type_0_requires_1_type_argument_s,
				c.typeToString(tpe, nil, true), len(typeParameters))
			return c.unknownType
		}

		var typeArguments []Type
		for i := 0; i < node.TypeArguments.Len(); i ++ {
			typeArguments = append(typeArguments, c.getTypeFromTypeNode(node.TypeArguments.At(i)))
		}
		// Because the type parameter is not empty, it must be generic
		return c.createTypeReference(tpe.(*genericType), typeArguments)
	}
	if node.TypeArguments != nil {
		c.error(node, diagnostic.M_Type_0_is_not_generic, c.typeToString(tpe, nil, true))
		return c.unknownType
	}
	return tpe
}

// Get type from reference to named type that cannot be generic (enum or type parameter)
func (c *checker) getTypeFromNonGenericTypeReference(node *ast.TypeReference, symbol *binder.Symbol) Type {
	if node.TypeArguments != nil {
		c.error(node, diagnostic.M_Type_0_is_not_generic, c.symbolToString(symbol, nil, 0, true))
		return c.unknownType
	}
	return c.getDeclaredTypeOfSymbol(symbol)
}

func (c *checker) isTypeParameterReferenceIllegalInConstraint(typeReferenceNode *ast.TypeReference, typeParameterSymbol *binder.Symbol) bool {
	var links = c.getNodeLinks(typeReferenceNode)
	if links.isIllegalTypeReferenceInConstraint != nil {
		return *links.isIllegalTypeReferenceInConstraint
	}

	// bubble up to the declaration
	var currentNode ast.Node = typeReferenceNode
	// forEach == exists
	for _, decl := range typeParameterSymbol.Declarations {
		if decl.Parent() == currentNode {
			break
		}

		currentNode = currentNode.Parent()
	}

	// if last step was made from the type parameter this means that path has started somewhere in constraint which is illegal
	links.isIllegalTypeReferenceInConstraint = xbase.NewBool(ast.IsTypeParameterDeclaration(currentNode))
	return *links.isIllegalTypeReferenceInConstraint
}

func (c *checker) checkTypeParameterHasIllegalReferencesInConstraintCheck(typeParameter *ast.TypeParameterDeclaration, n ast.Node) {
	if ast.IsTypeReference(n) && ast.IsIdentifier(n.(*ast.TypeReference).Name) {
		var links = c.getNodeLinks(n)
		if links.isIllegalTypeReferenceInConstraint == nil {
			var name = n.(*ast.TypeReference).Name.(*ast.Identifier)
			var symbol = c.resolveName(typeParameter, name.Value,
				binder.Type, nil, nil, "")
			if symbol != nil && symbol.Flags&binder.TypeParameter != 0 {
				var match = false
				for _, decl := range symbol.Declarations {
					if decl.Parent() == typeParameter.Parent() {
						match = true
						break
					}
				}
				links.isIllegalTypeReferenceInConstraint = xbase.NewBool(match)
			}
		}
		if links.isIllegalTypeReferenceInConstraint != nil && *links.isIllegalTypeReferenceInConstraint {
			c.error(typeParameter, diagnostic.M_Constraint_of_a_type_parameter_cannot_reference_any_type_parameter_from_the_same_type_parameter_list)
		}
	}
	ast.ForEachChild(n, func(node ast.Node) bool {
		c.checkTypeParameterHasIllegalReferencesInConstraintCheck(typeParameter, node)
		return false
	}, nil)
}

func (c *checker) checkTypeParameterHasIllegalReferencesInConstraint(typeParameter *ast.TypeParameterDeclaration) {
	if typeParameter.Constraint != nil {
		c.checkTypeParameterHasIllegalReferencesInConstraintCheck(typeParameter, typeParameter.Constraint)
	}
}

func (c *checker) getTypeFromTypeReferenceNode(node *ast.TypeReference) Type {
	var links = c.getNodeLinks(node)
	if links.resolvedType == nil {
		var tpe Type
		var symbol = c.resolveEntityName(node, node.Name, binder.Type, node)

		if symbol == nil {
			symbol = c.unknownSymbol
			tpe = c.unknownType
		} else if c.resolveSymbol(symbol).Flags&(binder.Class|binder.Interface) != 0 {
			tpe = c.getTypeFromClassOrInterfaceReference(node, c.resolveSymbol(symbol))
		} else {
			tpe = c.getTypeFromNonGenericTypeReference(node, c.resolveSymbol(symbol))
		}

		// Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the
		// type reference in checkTypeReferenceOrExpressionWithTypeArguments.
		links.resolvedSymbol = symbol
		links.resolvedType = tpe
	}
	return links.resolvedType
}

func (c *checker) getTypeFromBoundsNode(node *ast.BoundsType) Type {
	var links = c.getNodeLinks(node)
	if links.resolvedType == nil {
		var tpe Type
		if node.Bound == nil {
			tpe = c.noBoundsType
		} else if node.Bound.Token == token.ExtendsKeyword {
			var target = c.getTypeFromTypeNode(node.Type)
			if target != c.unknownType {
				tpe = c.getOutBoundsType(target)
			}

		} else if node.Bound.Token == token.SuperKeyword {
			var target = c.getTypeFromTypeNode(node.Type)
			if target != c.unknownType {
				tpe = c.getInBoundsType(target)
			}

		} else {
			panic("'getTypeFromBoundsNode' bounds is illegality.")
		}

		if tpe == nil {
			links.resolvedType = c.unknownType
		} else {
			links.resolvedType = tpe
		}
	}
	return links.resolvedType
}

func (c *checker) getSymbolInPackage(pkg *ast.Package, name string, meanings ... binder.SymbolFlags) *binder.Symbol {
	if pkg != nil && binder.GetLocalsOfNode(pkg).GetSymbol(name, meanings ...) != nil {
		return binder.GetLocalsOfNode(pkg).GetSymbol(name, meanings ...)
	} else {
		// report error
		c.error(nil, diagnostic.M_Cannot_find_global_type_0, name)
	}
	return nil
}

func (c *checker) getPackageSymbolInDefaultPackage(pkgPath string) *binder.Symbol {
	var tree = c.program.GetPackageTree(pkgPath)
	var pkg = c.program.GetPackageInDefaultLibrary(tree)
	if pkg != nil {
		return binder.GetSymbolOfNode(pkg)
	}

	c.error(nil, diagnostic.M_Cannot_find_package_0, pkgPath)
	return nil
}

func (c *checker) getTypeInDefaultPackage(pkgPath string, name string, arity int, meanings ... binder.SymbolFlags) ObjectType {
	var tree = c.program.GetPackageTree(pkgPath)
	var pkg = c.program.GetPackageInDefaultLibrary(tree)
	if pkg == nil {
		c.error(nil, diagnostic.M_Cannot_find_package_0, pkgPath)
		return c.unknownObjectType
	}

	// !!!
	var getTypeDeclaration = func(symbol *binder.Symbol) ast.Node {
		for _, decl := range symbol.Declarations {
			switch decl.(type) {
			case *ast.ClassDeclaration,
				*ast.InterfaceDeclaration,
				*ast.EnumDeclaration:
				return decl
			}
		}
		return nil
	}

	var symbol = c.getSymbolInPackage(pkg, name, meanings...)
	if symbol == nil {
		// error already report in 'getSymbolInPackage'.
		return c.unknownObjectType
	}

	var tpe = c.getDeclaredTypeOfSymbol(symbol)
	if tpe.Flags()&TFObjectLike == 0 {
		c.error(getTypeDeclaration(symbol), diagnostic.M_Global_type_0_must_be_a_class_or_interface_type, symbol.Name)
		return c.unknownObjectType
	}

	var interfaceType = tpe.(InterfaceType)
	var arityCur = 0
	if !isNull(interfaceType) && interfaceType.TypeParameters() != nil {
		arityCur = len(interfaceType.TypeParameters())
	}
	if arityCur != arity {
		c.error(getTypeDeclaration(symbol), diagnostic.M_Global_type_0_must_have_1_type_parameter_s, symbol.Name, arity)
		return c.unknownObjectType
	}
	return tpe.(ObjectType)
}

func (c *checker) createClassType(elementType Type) Type {
	return c.createTypeReference(c.globalClassType.(*genericType), []Type{elementType})
}

func (c *checker) createArrayType(elementType Type) Type {
	return c.createTypeReference(c.globalArrayType.(*genericType), []Type{elementType})
}

func (c *checker) getValueFormNumberLikeLiteralText(text string) *big.Float {
	if c.numberLikeValues[text] == nil {

		c.numberLikeValues[text] = new(big.Float)
		c.numberLikeValues[text].SetString(text)
	}
	return c.numberLikeValues[text]
}

func (c *checker) getLiteralOneType() *numberLiteralType {
	return c.getDigitalLiteralTypeForText("1")
}

func (c *checker) getLiteralMinusOneType() *numberLiteralType {
	return c.getDigitalLiteralTypeForText( "-1")
}

func (c *checker) getCharLiterTypeForText(text string) *numberLiteralType {
	var ch rune
	if len(text) > 0 {
		ch, _ = utf8.DecodeLastRuneInString(text)
	}

	var cleanString = string(ch)
	if c.charLiteralTypes[cleanString] == nil {
		c.charLiteralTypes[cleanString] = c.newCharLiteralType(ch)
	}
	return c.charLiteralTypes[cleanString]
}

func (c *checker) getDigitalLiteralTypeForText(text string) *numberLiteralType {
	if c.digitalLiteralTypes[text] == nil {
		c.digitalLiteralTypes[text] = c.newNumberLiteralType(text, false)
	}
	return c.digitalLiteralTypes[text]
}

func (c *checker) getDecimalLiteralTypeForText(text string) *numberLiteralType {
	if c.decimalLiteralTypes[text] == nil {
		c.decimalLiteralTypes[text] = c.newNumberLiteralType(text, true)
	}
	return c.decimalLiteralTypes[text]
}

func (c *checker) getNumberLiteralTypeForText(text string, isDecimal bool) LiteralType {
	if isDecimal {
		return c.getDecimalLiteralTypeForText(text)
	} else {
		return c.getDigitalLiteralTypeForText(text)
	}
}

func (c *checker) getStringLiteralTypeForType(text string) *stringLiteralType {
	if c.stringLiteralTypes[text] == nil {
		c.stringLiteralTypes[text] = c.newStringLiteralType(text)
	}
	return c.stringLiteralTypes[text]
}

func (c *checker) getTypeFromArrayTypeNode(node *ast.ArrayType) Type {
	var links = c.getNodeLinks(node)
	if links.resolvedType == nil {
		c.checkGrammarArrayType(node)
		links.resolvedType = c.createArrayType(c.getTypeFromTypeNode(node.Element))
	}
	return links.resolvedType
}

func (c *checker) getTypeFromElementAccessExpression(node *ast.ElementAccessExpression) Type {
	var links = c.getNodeLinks(node)
	if links.resolvedType == nil {
		if !isNull(node.ArgumentExpression) {
			c.grammarErrorOnNode(node.ArgumentExpression, diagnostic.M_No_expression_at_this_position)
		}
		links.resolvedType = c.createArrayType(c.getTypeFromNode(node.Expression))
	}
	return links.resolvedType
}

func (c *checker) getTypeFromNode(node ast.Node) Type{
	switch n := node.(type) {
	case *ast.Identifier,
		*ast.QualifiedName,
		*ast.SelectorExpression:
		if ast.IsIdentifier(n) {
			switch n.(*ast.Identifier).OriginalToken {
			case token.BooleanKeyword:
				return c.booleanType
			case token.CharKeyword:
				return c.charType
			case token.ByteKeyword:
				return c.byteType
			case token.ShortKeyword:
				return c.shortType
			case token.IntKeyword:
				return c.intType
			case token.LongKeyword:
				return c.longType
			case token.FloatKeyword:
				return c.floatType
			case token.DoubleKeyword:
				return c.doubleType
			}
		}	
			
		var symbol = c.getSymbolInfo(node)
		if symbol != nil {
			return c.getDeclaredTypeOfSymbol(symbol)
		}
	case *ast.IntrinsicType:
		return c.getTypeFromTypeNode(n)
	case *ast.TypeReference:
		return c.getTypeFromTypeNode(n)
	case *ast.ArrayType:
		return c.getTypeFromTypeNode(n)
	case *ast.BoundsType:
		return c.getTypeFromTypeNode(n)
	case *ast.ElementAccessExpression:
		return c.getTypeFromElementAccessExpression(n)
	}
	return c.unknownType
}

func (c *checker) getTypeFromTypeNode(node ast.Type) Type {
	switch n := node.(type) {
	case *ast.IntrinsicType:
		switch n.Token {
		case token.BooleanKeyword:
			return c.booleanType
		case token.CharKeyword:
			return c.charType
		case token.ByteKeyword:
			return c.byteType
		case token.ShortKeyword:
			return c.shortType
		case token.IntKeyword:
			return c.intType
		case token.LongKeyword:
			return c.longType
		case token.FloatKeyword:
			return c.floatType
		case token.DoubleKeyword:
			return c.doubleType
		default:
			panic("Unknown intrinsic type token kind")
		}
	case *ast.TypeReference:
		return c.getTypeFromTypeReferenceNode(n)
	case *ast.ArrayType:
		return c.getTypeFromArrayTypeNode(n)
	case *ast.BoundsType:
		return c.getTypeFromBoundsNode(n)
	default:
		return c.unknownType
	}
}

// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
// Argument node is ast.Node, because arrow function not a expression
func (c *checker) isContextSensitiveExpression(node ast.Expression) bool {
	switch n := node.(type) {
	case *ast.ArrowExpression:
		return c.isContextSensitiveFunctionLikeDeclaration(n.Declaration)
	case *ast.ParenthesizedExpression:
		return c.isContextSensitiveExpression(n.Expression)
	case *ast.ConditionalExpression:
		return c.isContextSensitiveExpression(n.WhenTrue) ||
			c.isContextSensitiveExpression(n.WhenFalse)
	case *ast.BinaryExpression:
		return n.Operator.Token == token.BarBar &&
			(c.isContextSensitiveExpression(n.Left) || c.isContextSensitiveExpression(n.Right))
	}
	return false
}

func (c *checker) isContextSensitiveFunctionLikeDeclaration(node ast.Signature) bool {
	// Functions with type parameters are not context sensitive.
	if node.GetTypeParameters() != nil {
		return false
	}

	var params = node.GetParameters()
	for i := 0; i < params.Len(); i ++ {
		var param = params.At(i).(*ast.ParameterDeclaration)
		if param.Type == nil {
			return true
		}
	}

	return false
}

// TYPE CHECKING

// Return the declaring class type of a property or undefined if property not declared in class
func (c *checker) getDeclaringTypeDeclaration(member *binder.Symbol) InterfaceType {
	if member.Parent != nil && member.Parent.Flags&(binder.ClassLike|binder.InterfaceLike) != 0 {
		return c.getDeclaredTypeOfSymbol(c.GetParentOfSymbol(member)).(InterfaceType)
	}
	return nil
}

func (c *checker) isSignatureParametersTypeIdentical(source *Signature, target *Signature) bool {
	if source == target {
		return true
	}

	if source.HasRestParameter != target.HasRestParameter {
		return false
	}

	if len(source.Parameters) != len(target.Parameters) {
		return false
	}

	if source.MinArgumentCount != target.MinArgumentCount {
		return false
	}

	if source.TypeParameters != nil && target.TypeParameters != nil {
		if len(source.TypeParameters) != len(target.TypeParameters) {
			return false
		}

		for i := 0; i < len(source.TypeParameters); i++ {
			var sourceApparentType = c.getApparentType(source.TypeParameters[i])
			var targetApparentType = c.getApparentType(target.TypeParameters[i])
			if !c.isTypeIdenticalTo(sourceApparentType, targetApparentType) {
				return false
			}
		}
	} else if source.TypeParameters != nil || target.TypeParameters != nil {
		return false
	}

	// Spec 1.0 Section 3.8.3 & 3.8.4:
	// M and N (the signatures) are instantiated using type Any as the type argument for all type parameters declared by M and N
	source = c.getErasedSignature(source)
	target = c.getErasedSignature(target)
	for i := 0; i < len(source.Parameters); i++ {
		var s Type
		if source.HasRestParameter && i == len(source.Parameters)-1 {
			s = c.getRestTypeOfSignature(source)
		} else {
			s = c.getTypeOfSymbol(source.Parameters[i])
		}
		var t Type
		if target.HasRestParameter && i == len(source.Parameters)-1 {
			t = c.getRestTypeOfSignature(target)
		} else {
			t = c.getTypeOfSymbol(target.Parameters[i])
		}
		if !c.isTypeIdenticalTo(s, t) {
			return false
		}
	}
	return true
}

func (c *checker) isSignatureOverrideTo(source *Signature, target *Signature) bool {
	if !c.isSignatureParametersTypeIdentical(source, target) {
		return false
	}

	var t = c.getReturnTypeOfSignature(target)
	var s = c.getReturnTypeOfSignature(source)
	return c.isTypeSubtypeOf(s, t)
}

func (c *checker) checkInterfaceExtendsMemberConflict(tpe InterfaceType, typeNode ast.Node) bool {

	return true
	//
	//if len(tpe.BaseTypes()) == 0 || len(tpe.BaseTypes()) == 1 {
	//	return true
	//}
	//
	//type info = struct {
	//	method         *binder.Symbol
	//	containingType Type
	//}
	//var seen = map[string]*info{}
	//for _, p := range tpe.DeclaredMembers().GetAll() {
	//	if p.Flags&binder.Method != 0 {
	//		seen[p.Name] = &info{p, tpe}
	//	}
	//}
	//
	//var ok = true
	//for i := 0; i < len(tpe.BaseTypes()); i++ {
	//	var baseType = tpe.BaseTypes()[i].Type
	//	var members = c.getFieldsOfType(baseType)
	//	for j := 0; j < len(members); j++ {
	//		var member = members[j]
	//		// Just need to check the method
	//		if member.Flags&binder.Method == 0 {
	//			continue
	//		}
	//
	//		if seen[member.Name] == nil {
	//			seen[member.Name] = &info{member, baseType}
	//		} else {
	//			var existing = seen[member.Name]
	//			var isInheritedMember = existing.containingType != tpe
	//			if isInheritedMember {
	//
	//				var sourceMethodType = c.getTypeOfSymbol(existing.method)
	//				var targetMethodType = c.getTypeOfSymbol(member)
	//
	//
	//
	//
	//				//&& !c.isMemberOverrideTo(existing.prop, member)
	//				ok = false
	//
	//				var typeName1 = c.typeToString(existing.containingType)
	//				var typeName2 = c.typeToString(baseType)
	//
	//				var errorInfo = diagnostic.ChainDiagnosticMessages(nil, diagnostic.M_Named_properties_0_of_types_1_and_2_are_not_identical, member.Name, typeName1, typeName2)
	//				errorInfo = diagnostic.ChainDiagnosticMessages(errorInfo, diagnostic.M_Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon, c.typeToString(tpe), typeName1, typeName2)
	//				c.addDiagnostic(utilities.CreateDiagnosticForNodeFromMessageChain(typeNode, errorInfo))
	//			}
	//		}
	//	}
	//}
	//
	//return ok
}

func (c *checker) checkObjectMembersAreOverrideable(source ObjectType, target ObjectType) bool {
	var parentDecl = source.Symbol().ValueDeclaration

	var ok = true
	for _, targetMember := range c.getMembersOfType(target) {
		// Just need to check the method
		if targetMember.Flags&binder.Method == 0 {
			continue
		}

		var sourceMember = c.getMemberOfType(source, targetMember.Name, targetMember.Flags)
		if sourceMember == targetMember {
			continue
		}

		if sourceMember == nil {
			panic("There should be no missing members here ")
		}

		var sourceMethodType = c.getTypeOfSymbol(sourceMember)
		var targetMethodType = c.getTypeOfSymbol(targetMember)

		var sourceSignatures = c.getSignaturesOfType(sourceMethodType, SIKCall)
		var targetSignatures = c.getSignaturesOfType(targetMethodType, SIKCall)

		// Used to mark signatures that have been used
		var useMark = make(map[*Signature]bool)

		// The missing signatures here have been completed,
		// we only need to check the return value type.
		outer:
		for _, t := range targetSignatures {
			// Private members cannot be inherited
			if c.getDeclarationModifierFlagsFromDeclaration(t.Declaration)&ast.MFPrivate != 0 {
				continue
			}

			for _, s := range sourceSignatures {

				// Skip already used signatures, performance optimization
				if useMark[s] {
					continue
				}

				// Only report errors of this type,
				// because the signature may be missing
				if s.Declaration == nil || s.Declaration.Parent() != parentDecl {
					continue
				}

				// If the signature parameters match, verify whether
				// the return value type is a subtype relationship
				if c.isSignatureParametersTypeIdentical(s, t) {
					var sourceType = c.getReturnTypeOfSignature(s)
					var targetType = c.getReturnTypeOfSignature(t)
					if !c.isTypeSubtypeOf(targetType, sourceType) {
						c.error(s.Declaration, diagnostic.M_The_return_type_0_cannot_be_assigned_to_the_return_type_1_of_the_parent_method,
							c.typeToString(sourceType, nil, true),
							c.typeToString(targetType, nil, true))
					}

					useMark[s] = true
					continue outer
				}
			}
		}
	}

	return ok
}

func (c *checker) isSupertypeOfEach(candidate Type, types []Type) bool {
	for _, t := range types {
		if candidate != t && !c.isTypeSubtypeOf(t, candidate) {
			return false
		}
	}
	return true
}

func (c *checker) getBestCommonType(types []Type, contextualType Type, candidatesOnly bool) Type {
	if contextualType != nil && c.isSupertypeOfEach(contextualType, types) {
		return contextualType
	}

	var result Type
	for _, t := range types {
		if c.isSupertypeOfEach(t, types) {
			result = t
			break
		}
	}

	if result == nil && !candidatesOnly {
		result = c.globalObjectType
	}
	return result
}

func (c *checker) isArrayType(tpe Type) bool {
	return tpe.Flags()&TFReference != 0 && tpe.(TypeReference).Target() == c.globalArrayType
}

func (c *checker) getArrayElementType(tpe Type) Type {
	switch n := tpe.(type) {
	case *typeReference:
		if n.target == c.globalArrayType &&
			len(n.typeArguments) > 0{
			return n.typeArguments[0]
		}
	}
	return c.unknownType
}

func (c *checker) getArrayTopType(tpe Type) Type {
	if c.isArrayType(tpe)  {
		return c.getArrayTopType(c.getArrayElementType(tpe))
	}
	return tpe
}

func (c *checker) forEachMatchingParameterType(source *Signature, target *Signature, callback func(s Type, t Type)) {
	var sourceMax = len(source.Parameters)
	var targetMax = len(target.Parameters)
	var count int
	if source.HasRestParameter && target.HasRestParameter {
		count = xmath.Max(sourceMax, targetMax)
		sourceMax--
		targetMax--
	} else if source.HasRestParameter {
		sourceMax--
		count = targetMax
	} else if target.HasRestParameter {
		targetMax--
		count = sourceMax
	} else {
		count = xmath.Min(sourceMax, targetMax)
	}
	for i := 0; i < count; i++ {
		var s Type
		if i < sourceMax {
			s = c.getTypeOfSymbol(source.Parameters[i])
		} else {
			s = c.getRestTypeOfSignature(source)
		}
		var t Type
		if i < targetMax {
			t = c.getTypeOfSymbol(target.Parameters[i])
		} else {
			t = c.getRestTypeOfSignature(target)
		}
		callback(s, t)
	}
}

// DECLARATION AND STATEMENT TYPE CHECKING
func (c *checker) checkPackageDeclaration(sourceFile *ast.SourceFile, node *ast.PackageDeclaration) {
	var pkg = ast.GetPackageOfNode(sourceFile)
	var name = c.program.GetPackagePath(pkg)
	if node == nil {
		if len(name) == 0 {
			return
		}

		// Missing package declaration
		c.grammarErrorAtPos(sourceFile, 0, 0, diagnostic.M_Missing_Package_declaration)
		return
	}

	var declName = c.getFullEntityName(node.Name)
	if name != declName {
		c.error(node, diagnostic.M_Package_name_0_does_not_correspond_to_the_file_path_1, declName, name)
	}
}

func (c *checker) getPackageTreeFromEntityName(node ast.Node) *ast.PackageTree {
	switch n := node.(type) {
	case *ast.Identifier:
		var name = n.Value
		var tree = c.program.GetPackageTree(name)
		if tree == nil {
			// Report error, current at first not found.
			c.error(node, diagnostic.M_Cannot_find_name_0, name)
		}
		return tree

	case *ast.QualifiedName:
		var leftTree = c.getPackageTreeFromEntityName(n.Left)
		if leftTree != nil {
			var name = c.getFullEntityName(node)
			var tree = c.program.GetPackageTree(name)
			if tree == nil {
				// Report error, current at first not found.
				c.error(n.Right, diagnostic.M_Cannot_find_name_0, n.Right.Value)
			}
			return tree
		}

	default:
		panic("'getResolvedPackageSymbol' ast kind is error.")
	}

	return nil
}

func (c *checker) getResolvedImportSymbol(node *ast.ImportDeclaration) *binder.Symbol {
	var links = c.getNodeLinks(node)
	if links.resolvedSymbol == nil {
		if !(node.Target == nil || ast.NodeIsMissing(node.Target)) {
			if tree := c.getPackageTreeFromEntityName(node.Path); tree != nil {
				var pro = ast.GetProjectFileOfNode(node)
				var pkg = c.program.GetPackage(pro, tree)
				if pkg != nil {
					var symbol = c.getSymbolOfNode(pkg)
					if ast.IsTokenNode(node.Target) && node.Target.(*ast.TokenNode).Token == token.Asterisk {
						links.resolvedSymbol = symbol
					} else {
						// Find symbol in package symbol
						var name = node.Target.(*ast.Identifier)
						links.resolvedSymbol = symbol.Exports.GetSymbol(name.Value, binder.Type)
					}
				} else {
					// Report error, current at first not found.
					c.error(node.Path, diagnostic.M_Cannot_find_package)
				}
			}
		}

		if links.resolvedSymbol == nil {
			links.resolvedSymbol = c.unknownSymbol
		}

	}
	return links.resolvedSymbol
}

func (c *checker) checkImportDeclaration(node *ast.ImportDeclaration) {
	c.getResolvedImportSymbol(node)
}

func (c *checker) checkTypeParameter(node *ast.TypeParameterDeclaration) {
	// Grammar Checking
	//if node.Name != nil {
	//	c.grammarErrorOnFirstToken(node.Name, diagnostic.M_Type_expected)
	//}

	c.checkSourceElement(node.Constraint)
	if c.produceDiagnostics {
		c.checkTypeParameterHasIllegalReferencesInConstraint(node)
		c.checkTypeNameIsReserved(node.Name, diagnostic.M_Type_parameter_name_cannot_be_0)
	}
	// TODO: Check multiple declarations are identical
}

func (c *checker) checkParameter(node *ast.ParameterDeclaration) {
	// Grammar checking
	_ = c.checkGrammarAnnotations(node) || c.checkGrammarModifiers(node)

	c.checkVariableLikeDeclaration(node)

	// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
	// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
	var symbol = binder.GetSymbolOfNode(node)
	if node.Dots != nil && !c.isArrayType(c.getTypeOfSymbol(symbol)) {
		c.error(node, diagnostic.M_A_rest_parameter_must_be_of_an_array_type)
	}
}

func (c *checker) checkThrowable(node ast.Type) {
	var tpe = c.getTypeFromTypeNode(node)
	if tpe.Flags()&TFObjectLike == 0 ||
		!c.hasBaseType(tpe.(ObjectType), c.globalThrowableType.(ObjectType)) {
		c.error(node, diagnostic.M_Type_0_is_not_assignable_to_type_1,
			c.typeToString(tpe, nil, true),
			c.typeToString(c.globalThrowableType, nil, true))
	}
}

func (c *checker) checkSignatureDeclaration(node ast.Signature) {
	typeParams, params, result, throws :=
		node.GetTypeParameters(), node.GetParameters(), node.GetType(), node.GetThrows()

	c.checkTypeParameters(typeParams)
	for i := 0; i < params.Len(); i ++ {
		c.checkParameter(params.At(i).(*ast.ParameterDeclaration))
	}
	for _, throw := range throws.Array() {
		c.checkThrowable(throw)
	}
	if result != nil {
		c.checkSourceElement(result)
	}
}

func (c *checker) checkFieldDeclaration(node *ast.FieldDeclaration) {
	// Grammar checking
	_ = c.checkGrammarAnnotations(node) || c.checkGrammarModifiers(node) || c.checkGrammarField(node)

	c.checkVariableLikeDeclaration(node)
}

func (c *checker) checkMethodDeclaration(node *ast.MethodDeclaration) {
	// Grammar checking
	_ = c.checkGrammarMethod(node)

	// Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration
	c.checkFunctionLikeDeclaration(node)

	// Abstract methods cannot have an implementation.
	// Extra checks are to avoid reporting multiple errors relating to the "abstractness" of the ast.
	if ast.GetModifierFlags(node) & ast.MFAbstract != 0 && node.Body != nil {
		c.error(node, diagnostic.M_Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, ast.DeclarationNameToString(node.Name))
	}
}

func (c *checker) checkConstructorDeclaration(node *ast.ConstructorDeclaration) {
	// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
	c.checkSignatureDeclaration(node)

	// Grammar check for checking only related to constructorDeclaration
	_ = c.checkGrammarConstructorTypeParameters(node) || c.checkGrammarConstructorTypeAnnotation(node)

	c.checkSourceElement(node.Body)
	c.registerForUnusedIdentifiersCheck(node)

	var symbol = binder.GetSymbolOfNode(node)
	var firstDeclaration = symbol.Declarations[0]

	// Only type check the symbol once
	if node == firstDeclaration {
		c.checkFunctionOrConstructorSymbol(symbol)
	}

	// exit early in the case of signature - super checks are not relevant to them
	if node.Body == nil {
		return
	}

	// exit early in the case of signature - super checks are not relevant to them
	if ast.NodeIsMissing(node.Body) {
		return
	}

	if !c.produceDiagnostics {
		return
	}

	// Constructors of classes with no extends clause may not contain super calls, whereas
	// constructors of derived classes must contain at least one super call somewhere in their function body.
	var containingClassDecl = node.Parent().(ast.ObjectLike)
	var thisSymbol = c.getSymbolOfNode(containingClassDecl)
	var declarationType = c.getDeclaredTypeOfSymbol(thisSymbol).(InterfaceType)
	for _, baseType := range c.getBaseTypes(declarationType) {
		if baseType.Kind == BTKExtends {
			var superCall = c.getSuperCallInConstructor(node)
			if superCall == nil && !c.isClassBaseTypeHasAvailableConstructor(declarationType, baseType.Type.(InterfaceType)){
				// Super call should be first
				if stmts := node.Body.Statements; stmts.Len() == 0 ||
					!ast.IsExpressionStatement(stmts.At(0)) ||
					!ast.IsSuperCall(stmts.At(0).(*ast.ExpressionStatement).Expression) {
					c.error(node, diagnostic.M_Super_call_must_be_the_first_statement_in_the_constructor_when_parent_class_has_no_parameter_constructor_or_default_constructor)
				}
			}

			break
		}
	}
}

func (c *checker) checkTypeReferenceNode(node *ast.TypeReference) {
	c.checkGrammarTypeArguments(node, node.TypeArguments)
	var t = c.getTypeFromTypeReferenceNode(node)
	if t != c.unknownType && node.TypeArguments != nil {
		// Do type argument local checks only if referenced type is successfully resolved
		var typeArgs = node.TypeArguments
		for i := 0; i < typeArgs.Len(); i ++ {
			c.checkSourceElement(typeArgs.At(i))
			var constraint = c.getConstraintOfTypeParameter(t.(TypeReference).Target().typeParams[i])
			if c.produceDiagnostics {
				var typeArgument = t.(TypeReference).TypeArguments()[i]
				var apparentType = c.getApparentType(typeArgument)
				if isNull(constraint) || constraint == c.noConstraintType {
					c.checkTypeAssignableTo(apparentType, c.globalObjectType, nil, typeArgs.At(i),
						diagnostic.M_Type_0_does_not_satisfy_the_constraint_1)
				} else {
					c.checkTypeAssignableTo(apparentType, constraint, nil, typeArgs.At(i),
						diagnostic.M_Type_0_does_not_satisfy_the_constraint_1)
				}
			}
		}
	}
}

func (c *checker) checkArrayType(node *ast.ArrayType) {
	c.getTypeFromArrayTypeNode(node)
}

func (c *checker) isPrivateWithinAmbient(node ast.Node) bool {
	return ast.GetModifierFlags(node) &ast.MFPrivate != 0 && ast.IsInAmbientContext(node)
}

func (c *checker) checkFunctionOrConstructorSymbol(symbol *binder.Symbol) {
	if !c.produceDiagnostics {
		return
	}
}

func (c *checker) getAnnotationTargets(tpe Type) annotationFlags {
	var symbol = tpe.Symbol()
	if symbol == nil {
		return noAllow
	}

	var links = c.getSymbolLinks(symbol)
	if links.annotationTargetFlags != nil {
		return *links.annotationTargetFlags
	}

	var decl = symbol.ValueDeclaration
	var arrayLiteral *ast.ArrayLiteralExpression
	// Find target annotation array literal argument
	outer:
	for _, declaration := range decl.GetAnnotations().Array() {
		var annotation = declaration.(*ast.Annotation)
		var target = c.getTypeFromTypeNode(annotation.Type)
		if target == c.globalTargetAnnotationType {
			var declaredMembers = target.(InterfaceType).DeclaredMembers()
			for _, argument := range annotation.Arguments.Array() {
				// Simple argument
				if ast.IsArrayLiteralExpression(argument) {
					arrayLiteral = argument.(*ast.ArrayLiteralExpression)
					break outer
				}

				// Normal annotation argument
				if !ast.IsAnnotationArgument(argument) {
					continue
				}
				var binaryExpr = argument.(*ast.BinaryExpression)
				var name = binaryExpr.Left.(*ast.Identifier)
				var value = binaryExpr.Right

				// It is possible to use alias symbol here
				var symbol = declaredMembers.GetSymbol(name.Value, binder.MethodLike)
				if !isNull(symbol) && c.resolveSymbol(symbol).Name == "value" && ast.IsArrayLiteralExpression(value) {
					arrayLiteral = value.(*ast.ArrayLiteralExpression)
					break outer
				}
			}
		}
	}

	// For each array literal
	if arrayLiteral != nil {
		var result annotationFlags
		for _, element := range arrayLiteral.Elements.Array() {
			if ast.IsSelectorExpression(element) {
				var selectorExpr = element.(*ast.SelectorExpression)
				var parentType = c.checkExpression(selectorExpr, nil)
				if parentType != c.globalElementType {
					continue
				}

				// Because the type of the determined expression is
				// 'ElementType' , so here you can safely get the'resolveSymbol'
				var resolveSymbol = c.getNodeLinks(selectorExpr).resolvedSymbol
				switch resolveSymbol.Name {
				case "TYPE":
					result |= typeAllow
				case "FIELD":
					result |= fieldAllow
				case "METHOD":
					result |= methodAllow
				case "PARAMETER":
					result |= parameterAllow
				case "CONSTRUCTOR":
					result |= constructorAllow
				case "LOCAL_VARIABLE":
					result |= localVariableAllow
				case "ANNOTATION_TYPE":
					result |= annotationTypeAllow
				case "PACKAGE":
					result |= packageAllow
				case "TYPE_PARAMETER":
					result |= typeParameterAllow
				case "TYPE_USE":
					result |= typeUseAllow
				}
			}
		}

		links.annotationTargetFlags = new(annotationFlags)
		*links.annotationTargetFlags = result
		return result
	}

	links.annotationTargetFlags = new(annotationFlags)
	*links.annotationTargetFlags = allAllow
	return *links.annotationTargetFlags
}

func (c *checker) annotationCanAt(node ast.Node, flags annotationFlags) bool {
	switch node.(type) {
	case *ast.ClassDeclaration,
		*ast.InterfaceDeclaration,
		*ast.EnumDeclaration:
		return flags&typeAllow != 0
	case *ast.FieldDeclaration:
		return flags&fieldAllow != 0
	case *ast.MethodDeclaration:
		return flags&methodAllow != 0
	case *ast.ParameterDeclaration:
		return flags&parameterAllow != 0
	case *ast.ConstructorDeclaration:
		return flags&constructorAllow != 0
	case *ast.VariableDeclaration:
		return flags&localVariableAllow != 0
	case *ast.AnnotationDeclaration:
		return flags&annotationTypeAllow != 0
	case *ast.PackageDeclaration:
		return flags&packageAllow != 0
	case *ast.TypeParameterDeclaration:
		return flags&typeUseAllow != 0
	}
	return false
}

func (c *checker) getOnlyAbstractValueMethodSignature(tpe Type) *Signature {
	var result *Signature
	for _, method := range c.getMethodsOfType(tpe) {
		var signatures = c.getMethodSignatureOfType(tpe, method.Name)
		for _, signature := range signatures {
			// Use the first signature of the method as the target signature
			if method.Name == "value" {
				result = signature
				break
			}

			if signature.Declaration.(*ast.MethodDeclaration).Default == nil {
				return nil
			}
			break
		}
	}

	return result
}

// Check the annotations of a ast
func (c *checker) checkAnnotation(node *ast.Annotation) {
	var tpe = c.getTypeFromTypeNode(node.Type)
	if tpe == c.unknownType {
		// Already report error
		return
	}

	if tpe.Flags()&TFAnnotation == 0 {
		c.error(node.Type, diagnostic.M_Annotation_type_expected)
		return
	}

	// Get the signature by the method name, and
	// then get the return value from the signature
	var getReturnType = func (methodName string) Type {
		var methodSymbol = c.getMethodOfType(tpe, methodName)
		if isNull(methodSymbol) {
			return nil
		}

		// Not self declared, may be inherited from Object.
		if methodSymbol.Parent != tpe.Symbol() {
			return nil
		}

		// It may be an alias symbol
		var realSymbol = c.resolveSymbol(methodSymbol)

		// Get self declared signatures
		var signatures = c.getSignaturesOfSymbol(realSymbol)
		if len(signatures) == 0 {
			return nil
		}

		return c.GetReturnTypeOfSignature(signatures[0])
	}

	var declType = tpe.(InterfaceType)
	var declaredMembers = declType.DeclaredMembers()
	// If the method has a default value or only the value
	// method has no default value, you can use simple arguemnt
	var canUseSimpleArgument = func() bool {
		var hasValueMethod = false
		var symbols = declaredMembers.GetSymbols(binder.Method)
		for _, symbol := range symbols {
			var declaration = symbol.Declarations[0].(*ast.MethodDeclaration)
			if declaration.Name.Value == "value" {
				hasValueMethod = true
				continue
			}

			if isNull(declaration.Default) {
				return false
			}
		}

		return hasValueMethod
	}

	var arguments = node.Arguments
	if valueType := getReturnType("value"); !isNull(valueType) &&
		arguments.Len() == 1 && ast.IsAnnotationArgumentValueNode(arguments.At(0)) && canUseSimpleArgument() {
		// Can omit name equal 'name = x'
		var argType = c.checkExpression(arguments.At(0), nil)
		c.checkTypeAssignableTo(argType, valueType, c.getNumberConstantValue(arguments.At(0)), arguments.At(0), nil)
	} else {
		var methods = declaredMembers.GetSymbols(binder.Method)

		// Argument must is named assignment
		var requireMap = make(map[string]*bool)
		for _, method := range methods {
			var declaration = method.Declarations[0].(*ast.MethodDeclaration)
			if !isNull(declaration.Default) {
				requireMap[method.Name] = xbase.NewBool(false)
			} else {
				requireMap[method.Name] = xbase.NewBool(true)
			}
		}

		for _, argument := range arguments.Array() {
			if ast.IsAnnotationArgument(argument) {
				var binaryExpr = argument.(*ast.BinaryExpression)
				var name = binaryExpr.Left.(*ast.Identifier)
				var value = binaryExpr.Right

				var symbol = declaredMembers.GetSymbol(name.Value, binder.MethodLike)
				if symbol == nil {
					c.error(name, diagnostic.M_Cannot_resolve_method_0, name)
					continue
				}

				c.getNodeLinks(name).resolvedSymbol = symbol
				var realSymbol = c.resolveSymbol(symbol)
				if requireMap[realSymbol.Name] == nil {
					c.error(name, diagnostic.M_Cannot_resolve_method_0, name)
					continue
				}

				var argType = c.checkExpression(value, nil)
				var returnType = getReturnType(realSymbol.Name)
				if c.checkTypeAssignableTo(argType, returnType, c.getNumberConstantValue(value), value, nil) != noMatch {
					// check value is constant

				}

				*requireMap[realSymbol.Name] = false
			} else {
				// format expect 'method name = value'
				c.error(argument, diagnostic.M_Wrong_format_need_method_name_equal_value)
			}
		}

		// Report missing method value
		for name, require := range requireMap {
			if *require {
				// Method not default value, and argument not set.
				c.error(node.Type, diagnostic.M_Method_0_Missing_value, name)
			}
		}
	}

	// Check annotation can be at current ast
	if ast.NodeCanBeDecorated(node.Parent()) && !c.annotationCanAt(node.Parent(), c.getAnnotationTargets(tpe)) {
		c.error(node.Type, diagnostic.M_Annotation_not_applicable_to_here)
	}
}

/** Check the annotations of a ast */
func (c *checker) checkAnnotations(node ast.Node){
	if node.GetAnnotations() == nil {
		return
	}

	// skip this check for nodes that cannot have decorators. These should have already had an error reported by
	// checkGrammarAnnotations.
	if !ast.NodeCanBeDecorated(node) {
		return
	}

	var annotations = node.GetAnnotations()
	for i := 0; i < annotations.Len(); i ++ {
		var annotation = annotations.At(i).(*ast.Annotation)
		c.checkAnnotation(annotation)
	}
}

func (c *checker) checkFunctionLikeDeclaration(node ast.FunctionLike) {
	c.checkAnnotations(node)
	c.checkSignatureDeclaration(node)

	var symbol = binder.GetSymbolOfNode(node)
	// Only type check the symbol once
	if node == symbol.Declarations[0] {
		c.checkFunctionOrConstructorSymbol(symbol)
		// run check once for the first declaration
		// run check on export symbol to check that modifiers agree across all exported declarations
		c.checkFunctionOrConstructorSymbol(symbol)
	}

	c.checkSourceElement(ast.GetFunctionLikeBody(node))
	if node.GetType() != nil {
		c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, c.getTypeFromTypeNode(node.GetType()))
	}
	c.registerForUnusedIdentifiersCheck(node)
}

func (c *checker) registerForUnusedIdentifiersCheck(node ast.Node) {
	//if (deferredUnusedIdentifierNodes) {
	//	deferredUnusedIdentifierNodes.push(ast)
	//}
}

func (c *checker) checkBlockStatement(node *ast.BlockStatement) {
	// Grammar checking for token.Block
	//if node.Kind == token.Block {
	//	c.checkGrammarStatementInAmbientContext(node)
	//}

	for _, statement := range node.Statements.Array() {
		c.checkSourceElement(statement)
	}
	if binder.GetLocalsOfNode(node) != nil {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

func (c *checker) checkStmts(stmts *ast.StatementList) {
	if stmts != nil {
		for i := 0; i < stmts.Len(); i ++ {
			c.checkSourceElement(stmts.At(i))
		}
	}
}

func (c *checker) checkDeclaredNamesNotShadowed(node ast.Declaration) {
	var nameNode = ast.GetDeclarationName(node)
	var symbol = c.getSymbolOfNode(node)
	// The error has been reported during the binding phase, and the error will not be reported repeatedly
	if symbol != nil && symbol.Flags&binder.Duplicate != 0 {
		return
	}

	var location ast.Node = node
	// The error is only reported when the parent of the symbol is different,
	//because if they are the same, the error has already been reported in the binding phase.

	if c.isTypeDeclaration(node) {
		// Look up to the parent until the package scope
		for location != nil && !ast.IsPackage(location) {
			var locals = binder.GetLocalsOfNode(location)
			if locals != nil {
				if findSymbol := c.getSymbol(locals, nameNode.Value, binder.Type); findSymbol != nil &&
					findSymbol != symbol && findSymbol.Parent != symbol.Parent {
					c.error(nameNode, diagnostic.M_Duplicate_type_name_0, nameNode.Value)
					return
				}
			}
			location = location.Parent()
		}
	}

	if ast.IsVariableDeclaration(node) {
		// Look up to the parent until the method
		for location != nil {
			var locals = binder.GetLocalsOfNode(location)
			if locals != nil {
				if findSymbol := c.getSymbol(locals, nameNode.Value, binder.Value); findSymbol != nil && findSymbol != symbol {
					c.error(nameNode, diagnostic.M_Duplicate_identifier_0, nameNode.Value)
					return
				}
			}

			if ast.IsMethodDeclaration(location) ||
				ast.IsConstructorDeclaration(location) {
				break
			}
			location = location.Parent()
		}
	}
}

func (c *checker) checkVariableLikeDeclaration(node ast.VariableLike) {
	c.checkGrammarVariableLikeDeclaration(node)
	c.checkAnnotations(node)
	c.checkDeclaredNamesNotShadowed(node)
	c.checkSourceElement(node.GetType())

	if c.produceDiagnostics {
		var symbol = binder.GetSymbolOfNode(node)
		var tpe = c.getTypeOfVariableOrParameterOrField(symbol)
		var init = node.GetInitializer()
		if !isNull(init) {
			if !(c.getNodeLinks(init).flags&typeChecked != 0) {
				// Use default messages
				var initType = c.checkExpressionCached(init, nil)
				c.checkTypeAssignableTo(initType, tpe, c.getNumberConstantValue(init), node, nil)
			}
		}
	}
}

func (c *checker) checkExpressionStatement(node *ast.ExpressionStatement) {
	// Grammar checking
	_ = c.checkGrammarStatementInAmbientContext(node) || c.checkGrammarExpressionStatement(node)

	c.checkExpression(node.Expression, nil)
}

func (c *checker) checkIfStatement(node *ast.IfStatement) {
	// Grammar checking
	c.checkGrammarStatementInAmbientContext(node)

	c.checkExpression(node.Condition, nil)
	c.checkSourceElement(node.ThenStatement)
	//if node.ThenStmt.Kind == token.EmptyStatement {
	//	c.error(node.ThenStmt, diagnostic.M_The_body_of_an_if_statement_cannot_be_the_empty_statement)
	//}
	c.checkSourceElement(node.ElseStatement)
}

func (c *checker) checkDoStatement(node *ast.DoStatement) {
	// Grammar checking
	c.checkGrammarStatementInAmbientContext(node)

	c.checkSourceElement(node.Body)
	c.checkExpression(node.Condition, nil)
}

func (c *checker) checkWhileStatement(node *ast.WhileStatement) {
	// Grammar checking
	c.checkGrammarStatementInAmbientContext(node)

	c.checkExpression(node.Condition, nil)
	c.checkSourceElement(node.Body)
}

func (c *checker) checkForStatement(node *ast.ForStatement) {
	// Grammar checking
	if !c.checkGrammarStatementInAmbientContext(node) {
		if node.Initializer != nil {
			if ast.IsVariableDeclaration(node.Initializer) {
				c.checkGrammarVariableLikeDeclaration(node.Initializer.(ast.VariableLike))
			} else {
				// c.checkGrammarExpressionList(ast.Initializer.ToExpressionList())
			}
		}
	}

	if node.Initializer != nil {
		if ast.IsVariableDeclaration(node.Initializer) {
			c.checkVariableLikeDeclaration(node.Initializer.(ast.VariableLike))
		} else {
			c.checkExpression(node.Initializer.(ast.Expression), nil)
		}
	}

	if node.Condition != nil {
		c.checkExpression(node.Condition, nil)
	}
	if node.Incrementor != nil {
		c.checkExpression(node.Incrementor, nil)
	}
	c.checkSourceElement(node.Body)
	if binder.GetLocalsOfNode(node) != nil {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

func (c *checker) isArrayTypeOrImplIterable(tpe Type) bool {
	return c.isArrayType(tpe) || c.hasBaseType(tpe, c.globalIterableType)
}

func (c *checker) getElementTypeFromArrayTypeOrIterator(tpe Type) Type {
	if c.isArrayType(tpe) {
		return c.getArrayElementType(tpe)
	} else if c.hasBaseType(tpe, c.globalIterableType) {
		var iteratedType = c.getBaseTypeWithTarget(tpe.(ObjectType), c.globalIterableType)
		if !isNull(iteratedType) {
			// iteratedType must a type reference, because it implement 'Iterable'
			return c.getTypeFromTypeArg(iteratedType.(*typeReference), 0)
		}
	}
	return nil
}

func (c *checker) checkForOfStatement(node *ast.ForOfStatement) {
	// Grammar checking
	c.checkGrammarForInStatement(node)

	var elementType Type
	var exprType = c.checkExpression(node.Expression, nil)
	if c.isArrayTypeOrImplIterable(exprType) {
		elementType = c.getElementTypeFromArrayTypeOrIterator(exprType)
	} else {
		c.error(node.Expression, diagnostic.M_For_each_type_must_array_type_or_implemnts_iterator)
	}

	c.checkVariableLikeDeclaration(node.VariableDeclaration)
	if !isNull(elementType) {
		var symbol = binder.GetSymbolOfNode(node.VariableDeclaration)
		var tpe = c.getTypeOfSymbol(symbol)
		c.checkTypeAssignableTo(elementType, tpe, nil, node.VariableDeclaration, nil)
	}
	c.checkSourceElement(node.Body)
}

func (c *checker) checkBreakOrContinueStatement(node *ast.BranchStatement) {
	// Grammar checking
	_ = c.checkGrammarStatementInAmbientContext(node) || c.checkGrammarBreakOrContinueStatement(node)

	// TODO: Check that target label is valid
}

func (c *checker) checkReturnStatement(node *ast.ReturnStatement) {
	if !isNull(node.Expression) && c.getNodeLinks(node.Expression).flags&typeChecked == 0 {
		var fun = ast.GetContainingFunction(node)
		if fun != nil {
			var returnType = c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(fun))

			if returnType == c.voidType {
				c.error(node.Expression, diagnostic.M_Cannot_return_a_value_from_a_method_with_void_result_type)
			} else {
				c.checkTypeAssignableTo(c.checkExpression(node.Expression, nil), returnType,
					c.getNumberConstantValue(node.Expression), node.Expression, nil)
			}
		}
	}
}

func (c *checker) checkSwitchStatement(node *ast.SwitchStatement) {
	var expressionType = c.checkExpression(node.Expression, nil)
	for i := 0; i < node.Body.Clauses.Len(); i ++ {
		var clause = node.Body.Clauses.At(i).(*ast.CaseClause)
		if c.produceDiagnostics && clause.Expression != nil {
			// TypeScript 1.0 spec (April 2014):5.9
			// In a 'switch' statement, each 'case' expression must be of a type that is assignable to or from the type of the 'switch' expression.
			var caseType = c.checkExpression(clause.Expression, nil)
			if !c.isTypeAssignableTo(expressionType, caseType, c.getNumberConstantValue(clause.Expression)) {
				// check 'expressionType isAssignableTo caseType' failed, try the reversed check and report errors if it fails
				c.checkTypeAssignableTo(caseType, expressionType,
					c.getNumberConstantValue(clause.Expression), clause.Expression, nil)
			}
		}
		c.checkStmts(clause.Statements)
	}
}

func (c *checker) checkLabelledStatement(node *ast.LabeledStatement) {
	c.checkSourceElement(node.Statement)
}

func (c *checker) checkThrowStatement(node *ast.ThrowStatement) {
	var throwType = c.checkExpression(node.Expression, nil)
	if !c.isCheckedExceptions(throwType) {
		c.error(node.Expression, diagnostic.M_Type_0_is_not_assignable_to_type_1,
			c.typeToString(throwType, nil, true),
			c.typeToString(c.globalThrowableType, nil, true))
	} else {
		c.checkExceptionIsCaptured(node, throwType)
	}
}

func (c *checker) checkTryStatement(node *ast.TryStatement) {
	c.checkBlockStatement(node.TryBlock)
	if node.CatchClauses != nil {
		for i := 0; i < node.CatchClauses.Len(); i ++ {
			var clause = node.CatchClauses.At(i).(*ast.CatchClause)
			c.checkVariableLikeDeclaration(clause.VariableDeclaration)
			c.checkBlockStatement(clause.Block)
		}
	}
	if node.FinallyBlock != nil {
		c.checkBlockStatement(node.FinallyBlock)
	}
}

func (c *checker) checkTypeNameIsReserved(name *ast.Identifier, message *diagnostic.Message) {
	// The predefined type keywords are reserved and cannot be used as names of user defined types.
	if name == nil {
		return
	}

	switch name.Value {
	case "any",
		"number",
		"boolean",
		"string",
		"void":
		c.error(name, message, name.Value)
	}
}

// Check each type parameter and check that list has no duplicate type parameter declarations
func (c *checker) checkTypeParameters(typeParameterDecls *ast.DeclarationList) {
	for i, n := range typeParameterDecls.Array() {
		var decl = n.(*ast.TypeParameterDeclaration)
		c.checkTypeParameter(decl)

		if c.produceDiagnostics {
			for j := 0; j < i; j++ {
				if binder.GetSymbolOfNode(typeParameterDecls.At(j)) == binder.GetSymbolOfNode(decl) {
					c.error(decl.Name, diagnostic.M_Duplicate_identifier_0, utilities.GetTextOfNode(decl.Name, false))
				}
			}
		}
	}
}

func (c *checker) checkClassDeclaration(node *ast.ClassDeclaration) {
	c.checkDeclaredNamesNotShadowed(node)
	c.checkClassLikeDeclaration(node)
	c.registerForUnusedIdentifiersCheck(node)
}

func (c *checker) checkAnonymousClassDeclaration(node *ast.BodyDeclaration) {
	c.checkClassLikeDeclaration(node)
}

func (c *checker) checkObjectAlias(tpe ObjectType) {
	c.resolveObjectTypeMembers(tpe)
	var tpeSymbol = tpe.Symbol()

	// Key: target symbol.  Value: alias symbol array
	var record = make(map[*binder.Symbol][]*binder.Symbol)
	var symbols = tpe.Members().GetSymbols(binder.AliasLike)
	for _, symbol := range symbols {
		record[c.resolveSymbol(symbol)] = append(record[c.resolveSymbol(symbol)], symbol)
	}

	outer:
	for _, aliasSymbols := range record {
		var aliasName string
		var thisSymbols []*binder.Symbol
		for _, symbol := range aliasSymbols {
			if symbol.Parent == tpeSymbol {
				thisSymbols = append(thisSymbols, symbol)
				continue
			}
			if len(aliasName) == 0 {
				aliasName = symbol.Name
				continue
			}
			if symbol.Name != aliasName {
				// Do not report this object of error if the
				// parent class has already made an error
				continue outer
			}
		}

		// This map contains alias symbols inherited from the parent class
		if len(aliasName) > 0 {
			// Check whether the alias symbol name of this
			// class is the same as the parent class
			for _, thisSymbol := range thisSymbols {
				if thisSymbol.Name == aliasName {
					continue
				}

				// Report alias symbols with different names
				for _, declaration := range thisSymbol.Declarations {
					var alias = declaration.(*ast.AliasTag)
					c.error(alias.Name, diagnostic.M_The_alias_name_0_is_inconsistent_with_the_alias_name_1_of_the_inherited, thisSymbol.Name, aliasName)
				}
			}

		} else {
			// Alias symbols are all in this object

			sort.Slice(thisSymbols, func(i, j int) bool {
				return thisSymbols[i].Declarations[0].Pos() <
					thisSymbols[j].Declarations[0].Pos()
			})

			// Based on the name of the first alias symbol
			var aliasName = thisSymbols[0].Name
			for i := 1; i < len(thisSymbols); i ++ {
				if thisSymbols[i].Name == aliasName {
					continue
				}

				// Report alias symbols with different names
				for _, declaration := range thisSymbols[i].Declarations {
					var alias = declaration.(*ast.AliasTag)
					c.error(alias.Name, diagnostic.M_Alias_name_0_is_inconsistent_1, thisSymbols[i].Name, aliasName)
				}
			}

		}
	}
}

func (c *checker) isSignatureIdenticalWhenTypeParameterErased(source *Signature, target *Signature) bool {
	if source == target {
		return true
	}

	if source.HasRestParameter != target.HasRestParameter {
		return false
	}

	if len(source.Parameters) != len(target.Parameters) {
		return false
	}

	if source.MinArgumentCount != target.MinArgumentCount {
		return false
	}

	// Do not consider type parameters
	for i := 0; i < len(source.Parameters); i++ {
		var s Type
		if source.HasRestParameter && i == len(source.Parameters)-1 {
			s = c.getRestTypeOfSignature(source)
		} else {
			s = c.getTypeOfSymbol(source.Parameters[i])
		}
		var t Type
		if target.HasRestParameter && i == len(source.Parameters)-1 {
			t = c.getRestTypeOfSignature(target)
		} else {
			t = c.getTypeOfSymbol(target.Parameters[i])
		}

		if s.Flags()&TFTypeParameter != 0 && t.Flags()&TFTypeParameter != 0 {
			continue
		}

		if s.Flags()&TFReference != 0 && t.Flags()&TFReference != 0 {
			s = s.(TypeReference).Target()
			t = s.(TypeReference).Target()
		}

		if !c.isTypeIdenticalTo(s, t) {
			return false
		}
	}
	return true
}

func (c *checker) checkObjectOverload (source ObjectType) {
	var tpe = c.resolveObjectTypeMembers(source)

	for _, method := range tpe.Methods() {
		var signatures = c.getInheritedSignaturesOfSymbol(method)
		var checked = make([]bool, len(signatures))
		for i := 0; i< len(signatures); i++ {
			if checked[i] {
				continue
			}
			checked[i] = true

			var conflicts = []*Signature{signatures[i]}
			for j := i + 1; j < len(signatures); j++ {
				if checked[j] {
					continue
				}

				if c.isSignatureIdenticalWhenTypeParameterErased(signatures[i], signatures[j]) {
					conflicts = append(conflicts, signatures[j])
					checked[j] = true
				}
			}

			if len(conflicts) > 1 {
				// Report errors, overload conflicts will only occur in the current class
				for _, conflict := range conflicts {
					c.error(conflict.Declaration, diagnostic.M_Overload_conflict)
				}
			}
		}
	}
}

func (c *checker) checkClassLikeImplements(declarationType InterfaceType, implementType Type) {
	var declaration = declarationType.Symbol().ValueDeclaration
	var modifiers = c.getDeclarationModifierFlagsFromDeclaration(declaration)
	if modifiers&ast.MFAbstract == 0 {
		var errorNode ast.Node = ast.GetDeclarationName(declaration)
		if isNull(errorNode) {
			errorNode = declaration
		}

		var links = c.getSymbolLinks(declarationType.Symbol())
		for symbol, signatures := range links.missingImplementSignatures {
			if len(signatures) > 0 {
				c.error(errorNode, diagnostic.M_Missing_implement_method_0, symbol.Name)
			}
		}
	}

	for _, targetMember := range c.getMembersOfType(implementType, binder.Method) {
		var sourceMember = c.getMemberOfType(declarationType, targetMember.Name, targetMember.Flags)
		if sourceMember == targetMember {
			continue
		}

		var sourceMethodType = c.getTypeOfSymbol(sourceMember)
		var targetMethodType = c.getTypeOfSymbol(targetMember)

		var sourceSignatures = c.getSignaturesOfType(sourceMethodType, SIKCall)
		var targetSignatures = c.getSignaturesOfType(targetMethodType, SIKCall)

		// Used to mark signatures that have been used
		var useMark = make(map[*Signature]bool)

		// The missing signatures here have been completed,
		// we only need to check the return value type.
	outer:
		for _, targetSignature := range targetSignatures {
			for _, sourceSignature := range sourceSignatures {

				// Skip already used signatures, performance optimization
				if useMark[sourceSignature] {
					continue
				}

				// Only report errors of this type, because the signature may be missing
				if sourceSignature.Declaration == nil || sourceSignature.Declaration.Parent() != declaration {
					continue
				}

				// If the signature parameters match, verify whether
				// the return value type is a subtype relationship
				if c.isSignatureParametersTypeIdentical(sourceSignature, targetSignature) {
					var sourceType = c.getReturnTypeOfSignature(sourceSignature)
					var targetType = c.getReturnTypeOfSignature(targetSignature)
					if !c.isTypeSubtypeOf(targetType, sourceType) {
						c.error(sourceSignature.Declaration, diagnostic.M_The_return_type_0_cannot_be_assigned_to_the_return_type_1_of_the_interface_method,
							c.typeToString(sourceType, nil, true),
							c.typeToString(targetType, nil, true))
					}
					// If the method is in the current class declaration, and the method
					// signature of the interface is implemented, We need to check for
					// accessibility, We only check the original symbol.
					if c.getDeclarationModifierFlagsFromDeclaration(sourceSignature.Declaration)&ast.MFPublic == 0 {
						c.error(sourceSignature.Declaration, diagnostic.M_When_implementing_interface_methods_accessibility_must_be_public)
					}

					useMark[sourceSignature] = true
					continue outer
				}
			}
		}
	}
}

func (c *checker) isClassBaseTypeHasAvailableConstructor(this InterfaceType, base Type) bool {
	var links = c.getNodeLinks(this.Symbol().ValueDeclaration)
	if links.baseTypeHasAvailableConstructor == nil {
		var thisSymbol = this.Symbol()
		var baseSymbol = base.Symbol()
		var baseConstructSymbol = baseSymbol.Members.GetSymbol(ast.ConstructorName, binder.Constructor)
		var baseConstructSignatures = c.getSignaturesOfSymbol(baseConstructSymbol)
		// No need to check if the base class is constructed by default
		if len(baseConstructSignatures) == 0 {
			links.baseTypeHasAvailableConstructor = xbase.NewBool(true)
		} else {
			// See if you can find a no parameter and accessible constructor
			for _, constructSignature := range baseConstructSignatures {
				if len(constructSignature.Parameters) == 0 && c.checkSignatureAccessibility(thisSymbol.ValueDeclaration,
					nil, baseConstructSymbol, constructSignature, false) {
					// We found a valid constructor
					links.baseTypeHasAvailableConstructor = xbase.NewBool(true)
					break
				}
			}
		}

		if links.baseTypeHasAvailableConstructor == nil {
			links.baseTypeHasAvailableConstructor = xbase.NewBool(false)
		}
	}

	return *links.baseTypeHasAvailableConstructor
}

func (c *checker) checkClassBaseTypeHasAvailableConstructor(this InterfaceType, base Type) {
	var thisSymbol = this.Symbol()
	var thisConstructSignatures = c.getSignaturesOfSymbol(thisSymbol.Members.GetSymbol(ast.ConstructorName, binder.Constructor))
	// If this class has a constructor, where there is no need to check
	if len(thisConstructSignatures) > 0 {
		return
	}

	if !c.isClassBaseTypeHasAvailableConstructor(this, base) {
		var baseSymbol = base.Symbol()

		var declaration = thisSymbol.ValueDeclaration
		c.error(declaration, diagnostic.M_There_is_no_default_constructor_available_in_0,
			c.symbolToString(baseSymbol, thisSymbol.ValueDeclaration, binder.Value, true))
	}
}

func (c *checker) checkClassLikeDeclaration(node ast.ObjectLike) {
	c.checkGrammarClassDeclarationHeritageClauses(node)
	c.checkAnnotations(node)
	var declName = ast.GetDeclarationName(node)
	if declName != nil {
		c.checkTypeNameIsReserved(declName, diagnostic.M_Class_name_cannot_be_0)
	}

	if node.GetTypeParameters() != nil {
		c.checkTypeParameters(node.GetTypeParameters())
	}

	var symbol = c.getSymbolOfNode(node)
	var declarationType = c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)
	for _, baseType := range c.getBaseTypes(declarationType) {
		if baseType.Kind == BTKExtends {
			// The anonymous class has already called the constructor
			if !ast.IsBodyDeclaration(node) {
				c.checkClassBaseTypeHasAvailableConstructor(declarationType, baseType.Type)
			}
			c.checkObjectMembersAreOverrideable(declarationType, baseType.Type)
		} else {
			c.checkClassLikeImplements(declarationType, baseType.Type)
		}
	}

	c.checkObjectAlias(declarationType)
	c.checkObjectOverload(declarationType)

	var members = node.GetMembers()
	for i := 0; i < members.Len(); i ++ {
		c.checkSourceElement(members.At(i))
	}
}

func (c *checker) checkEnumItem(node *ast.EnumItemDeclaration) {
	c.resolveSignature(node)
}

func (c *checker) checkEnumDeclaration(node *ast.EnumDeclaration) {
	c.checkGrammarEnumDeclaration(node)
	c.checkDeclaredNamesNotShadowed(node)
	c.checkTypeNameIsReserved(node.Name, diagnostic.M_Enum_name_cannot_be_0)
	c.checkClassLikeDeclaration(node)
	c.registerForUnusedIdentifiersCheck(node)

	for i := 0; i < node.EnumItems.Len(); i ++ {
		c.checkSourceElement(node.EnumItems.At(i))
	}
}

func (c *checker) getTargetSymbol(symbol *binder.Symbol) *binder.Symbol {
	// if symbol is instantiated it'symbol flags are not copied from the 'target'
	// so we'll need to get back original 'target' symbol to work with correct set of flags
	if c.getCheckFlags(symbol)&instantiated != 0 {
		return c.getSymbolLinks(symbol).target
	}
	return symbol
}

func (c *checker) getTypeDeclarationOfSymbol(symbol *binder.Symbol) ast.ObjectLike {
	for _, decl := range symbol.Declarations {
		if ast.IsClassLike(decl) {
			return decl.(ast.ObjectLike)
		}
	}
	return nil
}

func (c *checker) areTypeParametersIdentical(list1 []*ast.TypeParameterDeclaration, list2 []*ast.TypeParameterDeclaration) bool {
	if list1 == nil && list2 == nil {
		return true
	}
	if list1 == nil || list2 == nil || len(list1) != len(list2) {
		return false
	}
	// TypeScript 1.0 spec (April 2014):
	// When a generic interface has multiple declarations,  all declarations must have identical type parameter
	// lists, i.e. identical type parameter names with identical constraints in identical order.
	for i := 0; i < len(list1); i++ {
		var tp1 = list1[i]
		var tp2 = list2[i]

		if utilities.GetTextOfNode(tp1.Name, false) != utilities.GetTextOfNode(tp2.Name, false) {
			return false
		}
		if tp1.Constraint == nil && tp2.Constraint == nil {
			continue
		}
		if tp1.Constraint == nil || tp2.Constraint == nil {
			return false
		}
		if !c.isTypeIdenticalTo(c.getTypeFromTypeNode(tp1.Constraint), c.getTypeFromTypeNode(tp2.Constraint)) {
			return false
		}
	}
	return true
}

func (c *checker) checkInterfaceDeclaration(node *ast.InterfaceDeclaration) {
	// Grammar checking
	_ = c.checkGrammarAnnotations(node) || c.checkGrammarModifiers(node) || c.checkGrammarInterfaceDeclaration(node)

	c.checkDeclaredNamesNotShadowed(node)
	c.checkTypeParameters(node.TypeParameters)
	if c.produceDiagnostics {
		c.checkTypeNameIsReserved(node.Name, diagnostic.M_Interface_name_cannot_be_0)
		var symbol = binder.GetSymbolOfNode(node)
		var tpe = c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)
		// run subsequent checks only if first set succeeded
		if c.checkInterfaceExtendsMemberConflict(tpe, node.Name) {
			for _, baseType := range c.getBaseTypes(tpe) {
				c.checkTypeSubtypeOf(tpe, baseType.Type, node.Name,
					diagnostic.M_Interface_0_incorrectly_extends_interface_1)
			}
		}
	}

	if extends := ast.GetClassExtendsHeritageClauseElement(node); extends != nil{
		c.checkTypeReferenceNode(extends)
	}
	for i := 0; i < node.Members.Len(); i ++ {
		c.checkSourceElement(node.Members.At(i))
	}
}

func (c *checker) checkAnnotationDeclaration(node *ast.AnnotationDeclaration) {
	// Grammar checking
	_ = c.checkGrammarAnnotations(node) || c.checkGrammarModifiers(node) || c.checkGrammarAnnotationDeclaration(node)
	c.checkDeclaredNamesNotShadowed(node)
	c.checkAnnotations(node)

	for i := 0; i < node.Members.Len(); i ++ {
		c.checkSourceElement(node.Members.At(i))
	}
	var symbol = c.getSymbolOfNode(node)
	var tpe = c.getDeclaredTypeOfSymbol(symbol).(InterfaceType)
	c.checkObjectAlias(tpe)
}

func (c *checker) getNodeCheckFlags(node ast.Node) bool {
	return false
	//ast = getParseTreeNode(ast)
	//return ast ? getNodeLinks(ast).flags : undefined
}

func (c *checker) getFirstIdentifier(node *ast.QualifiedName) *ast.Identifier {
	var cur ast.Node = node
	for true {
		if name, ok := cur.(*ast.QualifiedName); ok {
			cur = name.Left
			continue
		}
		break
	}
	return cur.(*ast.Identifier)
}

func (c *checker) checkDocRoot(node *ast.DocRoot) {
	for _, document := range node.Children.Array() {
		c.checkSourceElement(document)
	}
}

func (c *checker) checkSeeTag(node *ast.SeeTag) {
	c.checkExpression(node.Expression, nil)
}

func (c *checker) checkExceptionTag(node *ast.ExceptionTag) {
	c.getTypeFromNode(node.Type)
}

func (c *checker) checkCodeLink(node *ast.CodeLink) {
	c.checkExpression(node.Expression, nil)
}

func (c *checker) checkSourceElement(node ast.Node) {
	if isNull(node) {
		return
	}

	for _, document := range node.GetDocuments() {
		c.checkSourceElement(document)
	}

	switch n := node.(type) {
	case *ast.TypeParameterDeclaration:
		c.checkTypeParameter(n)
	case *ast.ParameterDeclaration:
		c.checkParameter(n)
	case *ast.FieldDeclaration:
		c.checkFieldDeclaration(n)
	case *ast.MethodDeclaration:
		c.checkMethodDeclaration(n)
	case *ast.ConstructorDeclaration:
		c.checkConstructorDeclaration(n)
	case *ast.TypeReference:
		c.checkTypeReferenceNode(n)
	case *ast.DeclarationStatement:
		c.checkSourceElement(n.Declaration)
	case *ast.BlockStatement:
		c.checkBlockStatement(n)
	case *ast.ExpressionStatement:
		c.checkExpressionStatement(n)
	case *ast.IfStatement:
		c.checkIfStatement(n)
	case *ast.DoStatement:
		c.checkDoStatement(n)
	case *ast.WhileStatement:
		c.checkWhileStatement(n)
	case *ast.ForStatement:
		c.checkForStatement(n)
	case *ast.ForOfStatement:
		c.checkForOfStatement(n)
	case *ast.BranchStatement:
		c.checkBreakOrContinueStatement(n)
	case *ast.ReturnStatement:
		c.checkReturnStatement(n)
	case *ast.SwitchStatement:
		c.checkSwitchStatement(n)
	case *ast.LabeledStatement:
		c.checkLabelledStatement(n)
	case *ast.ThrowStatement:
		c.checkThrowStatement(n)
	case *ast.TryStatement:
		c.checkTryStatement(n)
	case *ast.ArrayType:
		c.checkArrayType(n)
	case *ast.VariableDeclaration:
		c.checkVariableLikeDeclaration(n)
	case *ast.ClassDeclaration:
		c.checkClassDeclaration(n)
	case *ast.EnumDeclaration:
		c.checkEnumDeclaration(n)
	case *ast.EnumItemDeclaration:
		c.checkEnumItem(n)
	case *ast.InterfaceDeclaration:
		c.checkInterfaceDeclaration(n)
	case *ast.AnnotationDeclaration:
		c.checkAnnotationDeclaration(n)
	case *ast.DocRoot:
		c.checkDocRoot(n)
	case *ast.SeeTag:
		c.checkSeeTag(n)
	case *ast.ExceptionTag:
		c.checkExceptionTag(n)
	case *ast.CodeLink:
		c.checkCodeLink(n)
	}
}

// Function and class expression bodies are checked after all statements in the enclosing body. This is
// to ensure constructs like the following are permitted:
//     const foo = function () {
//        const s = foo()
//        return "hello"
//     }
// Here, performing a full type check of the body of the function expression whilst in the process of
// determining the type of foo would cause foo to be given type any because of the recursive reference.
// Delaying the type check of the body ensures foo has been assigned a type.
func (c *checker) checkNodeDeferred(node ast.Node) {
	if c.deferredNodes == nil {
		return
	}
	for _, deferredNode := range c.deferredNodes {
		if deferredNode == node {
			return
		}
	}
	c.deferredNodes = append(c.deferredNodes, node)
}

func (c *checker) checkDeferredNodes() {
	for i := 0; i < len(c.deferredNodes); i ++ {
		var node = c.deferredNodes[i]
		switch n := node.(type) {
		case *ast.ArrowFunctionDeclaration:
			c.checkArrowFunctionDeferred(n)
		default:
			panic("Unknown deferred node type")
		}
	}
}

// Fully type check a session file and collect the relevant diagnostics.
func (c *checker) checkSourceFile(node *ast.SourceFile) {
	var links = c.getNodeLinks(node)
	if links.flags&typeChecked == 0 {
		performance.Mark("beforeCheck")
		c.checkPackageDeclaration(node, node.PackageDeclaration)
		for i := 0; i < node.Imports.Len(); i ++ {
			c.checkImportDeclaration(node.Imports.At(i).(*ast.ImportDeclaration))
		}
		for i := 0; i < node.Declarations.Len(); i ++ {
			c.deferredNodes = make([]ast.Node, 0)
			c.checkSourceElement(node.Declarations.At(i))
			c.checkDeferredNodes()
			c.deferredNodes = nil
		}
		links.flags |= typeChecked
		performance.Mark("afterCheck")
		performance.Measure("Check", "beforeCheck", "afterCheck")
	}
}

